#!/bin/sh
touch setup systest; chmod 700 setup systest
#!/bin/sh
# Determine how echo suppresses new-line
echo "a\c" > /tmp/echo
if [ "`grep c /tmp/echo`" = "a\c" ]; then
  n='-n'
  c=''
else
  n=''
  c='\c'
fi
rm /tmp/echo

echo
echo \	\	\	UNIX ListServer 6.0
echo \	\	\	-------------------
echo
if [ -d osrc -o -d ohelp -o -d oarchives -o -f oconfig -o -f o.ignored \
     -o -f oowners -o -f ounwanted.hosts -o -f opriv.hosts -o -d outil \
     -o -f owelcome.live ]; then
  echo Old system image found:
  echo
  ls -CFd o[a-w\.]*
  echo
  echo The current system will be moved to files/dirs starting with \'o\'
  echo as shown above and confirmation is required to remove that old image.
  echo $n "Proceed [y]? $c"
  read x
  if [ "$x" = "n" -o "$x" = "N" ]; then
    exit 0
  fi
fi
if [ -d src ]; then
  echo WARNING: Moving src/ to osrc/
  rm -rf osrc
  rmdir osrc > /dev/null 2>&1
  mv src osrc
fi
if [ ! -d src ]; then
  mkdir src
fi
if [ ! -d src/ansi ]; then
  mkdir src/ansi
fi
if [ ! -d src/nonansi ]; then
  mkdir src/nonansi
fi
if [ ! -d gateway ]; then
  mkdir gateway
fi
if [ ! -d doc ]; then
  mkdir doc
fi
if [ -d help ]; then
  echo WARNING: Moving help/ to ohelp/
  rm -rf ohelp
  rmdir ohelp > /dev/null 2>&1
  mv help ohelp
fi
mkdir help
if [ -d util ]; then
  echo WARNING: Moving util/ to outil/
  rm -rf outil
  rmdir outil > /dev/null 2>&1
  mv util outil
fi
mkdir util
if [ -d archives ]; then
  echo WARNING: Moving archives/ to oarchives/
  rm -rf oarchives
  rmdir oarchives > /dev/null 2>&1
  mv archives oarchives
fi
mkdir archives
mkdir archives/listserver
mkdir archives/listserver/example.dat
mkdir archives/pub
mkdir archives/pub/unix
mkdir archives/pub/private
mkdir archives/unix
mkdir archives/iul
if [ -f config ]; then
  echo WARNING: Moving config to oconfig
  mv config oconfig
fi
if [ -f owners ]; then
  echo WARNING: Moving owners to oowners
  mv owners oowners
fi
if [ -f .ignored ]; then
  echo WARNING: Moving .ignored to o.ignored
  mv .ignored o.ignored
fi
if [ -f unwanted.hosts ]; then
  echo WARNING: Moving unwanted.hosts to ounwanted.hosts
  mv unwanted.hosts ounwanted.hosts
fi
if [ -f welcome.live ]; then
  echo WARNING: Moving welcome.live to owelcome.live
  mv welcome.live owelcome.live
fi
echo x - peer
sed 's/^X//' >peer <<'*-*-END-of-peer-*-*'
X#!/bin/sh
X# Shell script to add a peer list to a mailing list. 
X#
X# Usage: peer <LIST_ALIAS> <remote alias> <remote peer email address> 
X#	      <remote peer ListServer email address>
X# where:
X# LIST_ALIAS: a local list alias, capitalized, that is to be connected to
X#  the new peer.
X# remote alias: the peer's list alias on the remote machine
X# remote peer email address: full email address of the peer list to be added
X# remote peer ListServer email address: full email address of the server
X#   that handles the remote peer.
X
XPROG=`basename $0`
Xif [ $# -lt 4 ]; then
X  echo Usage: $PROG \<LIST_ALIAS\> \<remote alias\> \
X\<remote $PROG email address\> \<remote $PROG ListServer email address\>
X  exit 1
Xfi
X
XARGS=$PROG\ $*
XDIR=/usr/server/lists/`echo $1 | tr '[a-z]' '[A-Z]'`
XREMOTE_ALIAS=`echo $2 | tr '[A-Z]' '[a-z]'`
XEMAIL=`echo $3 | tr '[A-Z]' '[a-z]'`
XREMOTE_SERVER=`echo $4 | tr '[A-Z]' '[a-z]'`
XREPORT=/usr/server/.report.server
XCONFIG=/usr/server/config
XMODE=NOACK
XFILE=$DIR/.peers
Xshift; shift; shift; shift
X
Xif [ ! -d $DIR ]; then
X  echo $DIR: no such list
X  exit 2
Xfi
Xif [ ! -r $CONFIG ]; then
X  echo Cannot open $CONFIG
X  exit 2
Xfi
Xif [ ! -r $FILE -o ! -w $FILE ]; then
X  echo Cannot open $FILE for read/write
X  exit 2
Xfi
X
Xecho >> $REPORT
Xecho --- NEW MAIL HAS ARRIVED --- >> $REPORT
Xecho $ARGS >> $REPORT
XLOCAL=`grep -i "^list *$REMOTE_ALIAS *$EMAIL" $CONFIG | awk '{ print $2 }'`
Xif [ "$LOCAL" = "" ]; then
X  LOCAL=`grep -i "^server\  *$EMAIL" $CONFIG | awk '{ print $2 }'`
X  txt=server
Xelse
X  txt=list
Xfi
Xif [ "$LOCAL" != "" ]; then
X  echo Cannot add local $txt $LOCAL as $PROG to $FILE >&1 | tee -a $REPORT
Xelse
X  if [ "`grep -i $EMAIL $FILE`" != "" ]; then
X    echo $EMAIL\: Already subscribed as a $PROG in $FILE >&1 | tee -a $REPORT
X  else
X    echo $EMAIL $MODE $REMOTE_ALIAS $REMOTE_SERVER >> $FILE
X  fi
Xfi
Xecho Time/Date: `date` >> $REPORT
Xexit 0
*-*-END-of-peer-*-*
echo x - news
sed 's/^X//' >news <<'*-*-END-of-news-*-*'
X#!/bin/sh
X# Shell script to add a news group to a mailing list.
X#
X# Usage: news <LIST_ALIAS> <news group> <news email address> <news mode>
X#
X# where:
X# LIST_ALIAS: a local list alias, capitalized, that is to be connected to
X#  the news group.
X# news group: the news group name (e.g. alt.sex)
X# news email address: full email address of the news source.
X# news mode: "receive" if the local list will only be receiving articles,
X#  or "send_receive" if the local list will post to the news group as
X#  well.
X
XPROG=`basename $0`
Xif [ $# -lt 4 ]; then
X  echo Usage: $PROG \<LIST_ALIAS\> \<news group\> \<$PROG email address\> \<$PROG mode\>
X  exit 1
Xfi
X
XARGS=$PROG\ $*
XDIR=/usr/server/lists/`echo $1 | tr '[a-z]' '[A-Z]'`
XREPORT=/usr/server/.report.server
XCONFIG=/usr/server/config
XIGNORED=/usr/server/.ignored
XFILE=$DIR/.news
XGROUP=`echo $2 | tr '[A-Z]' '[a-z]'`
XEMAIL=`echo $3 | tr '[A-Z]' '[a-z]'`
Xshift; shift; shift
X
Xif [ ! -d $DIR ]; then
X  echo $DIR: no such list
X  exit 2
Xfi
Xif [ ! -r $CONFIG ]; then
X  echo Cannot open $CONFIG
X  exit 2
Xfi
Xif [ ! -r $FILE -o ! -w $FILE ]; then
X  echo Cannot open $FILE for read/write
X  exit 2
Xfi
Xif [ "$1" = "receive" ]; then
X  MODE=POSTPONE
Xelif [ "$1" = "send_receive" ]; then
X  MODE=NOACK
Xelse
X  echo Token $1 not recognized
X  exit 2
Xfi
X
Xecho >> $REPORT
Xecho --- NEW MAIL HAS ARRIVED --- >> $REPORT
Xecho $ARGS >> $REPORT
XLOCAL=`grep -i "^list *$REMOTE_ALIAS *$EMAIL" $CONFIG | awk '{ print $2 }'`
Xif [ "$LOCAL" = "" ]; then
X  LOCAL=`grep -i "^server\  *$EMAIL" $CONFIG | awk '{ print $2 }'`
X  txt=server
Xelse
X  txt=list
Xfi
Xif [ "$LOCAL" != "" ]; then
X  echo Cannot add local $txt $LOCAL as $PROG to $FILE >&1 | tee -a $REPORT
Xelse
X  if [ "`grep -i $EMAIL $FILE`" != "" ] || [ "`grep -i $GROUP $FILE`" != "" ]
X  then
X    echo $EMAIL\: Already subscribed as a $PROG group in $FILE >&1 | \
X         tee -a $REPORT
X  else
X    echo $EMAIL $MODE $GROUP >> $FILE
X    echo $EMAIL >> $IGNORED
X  fi
Xfi
Xecho Time/Date: `date` >> $REPORT
Xexit 0
*-*-END-of-news-*-*
echo x - doc/catmail.1
sed 's/^X//' >doc/catmail.1 <<'*-*-END-of-doc/catmail.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH catmail 1 "UNIX ListServer"
X.SH NAME
X\fBcatmail\fP \- append incoming mail to UNIX ListServer system files
X.SH SYNOPSIS
X\fBcatmail\fP {<\fB-L LIST_ALIAS\fP [\fB-m\fP]> | <\fB-r\fP>} [\fB-f\fP]
X.SH DESCRIPTION
X\fIcatmail\fP is used to redirect incoming mail to the UNIX ListServer system
Xto the appropriate
Xfiles, according to the flags specified. The files affected are
XHOMEDIR/requests (a repository for requests), HOMEDIR/lists/*/mail
X(public messages to be distributed) and HOMEDIR/lists/*/moderated
X(messages to be screened out by a moderator). \fIcatmail\fP reports the
Xuser id and user name that is currently executing it; the setuid bit
Xhas to be set when installed. \fIcatmail\fP first locks the output file
Xbefore appending to it; if the file cannot be locked after 3 minutes, the
Xmail is saved under HOMEDIR/lost+found or HOMEDIR/lists/*/lost+found;
Xboth \fIlist\fP and \fIlistserv\fP
Xlock their mail files while copying them to a safe place.
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-L LIST_ALIAS [-m]
XIf the -m flag is not specified, append to
XHOMEDIR/lists/\fILIST_ALIAS\fP/mail; otherwise append to
XHOMEDIR/lists/\fILIST_ALIAS\fP/moderated (see list(1)).
X.TP
X-r
XAppend to HOMEDIR/requests (overrides -L).
X.TP
X-f
XReformat the message in the process: all lines in the message except the first
Xstarting with "From " are converted to ">From ".
X(see also SYSTEM\ SETUP in server(1)).
X.SH SEE\ ALSO
Xlist(1), listserv(1), server(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/catmail.1-*-*
echo x - doc/farch.1
sed 's/^X//' >doc/farch.1 <<'*-*-END-of-doc/farch.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH farch 1 "UNIX ListServer"
X.SH NAME
X\fBfarch\fP \- archive files under the UNIX ListServer system
X.SH SYNOPSIS
X\fBfarch\fP {[\fB-n\fP] [\fB-b\fP | \fB-B\fP] [\fB-s\ size\fP] [\fB-d\ dir\fP] [\fB-p\ password\fP] [\fB-D\ description\fP]\ [\fB-t\ file\fP]} | {[\fB-r\fP]} [\fB-a\ archive | path-to-archive\fP] <\fBfiles\fP>
X.SH DESCRIPTION
X\fIfarch\fP is used to archive files under the UNIX ListServer system 
Xor remove files already achived, with support provided for private archives.
XArchives reside under HOMEDIR/archives as subdirectories; the default is
X\fIlistserver\fP,
Xand it is also the master archive. Archives are hierarchically
Xstructured, and each archive has at least two files in its directory:
X.TP
XINDEX
XA list of all subarchives including itself; the format is one line per
Xarchive with the archive's name followed by the archive's full path, followed
Xby an optional password:
X.sp
Xarchive-name full-path-to-archive-directory [password]
X.sp
XThe first entry should be the archive itself;
Xeach first level subarchive is listed next; then each second level
Xsubarchive, starting with the first first-level-archive's subarchives,
Xfollowed by the second first-level-archive's subarchives, etc. No sibling
Xarchives may have the same name, although there is no such restriction
Xfor archives on different levels, or "cousin" archives. The latter archives
Xare distinguished by the relative path needed to access them (see
X\fIserver(1)\fP for more information).
X.TP
XDIR
XA list of files available from that archive; the format is one line
Xper file with the file name followed by the number of parts it may be
Xsplit into, followed by the number of bytes of each of the parts, followed
Xby the full path to the directory these parts can be found, followed
Xby a short descriptive message about the file (optional). The descriptive
Xmessage may span several lines; each line (except the last) should end 
Xwith a '\\':
X.sp
X.nf
Xfile nparts size-of-each-part+ full-path-to-directory [description [\\]]
X[multiple description lines [\\]]*
X.fi
X.sp
XThe number of parts the file is split into may be -1 to indicate that
Xthis is a binary file.
X.PP
X\fIfarch\fP reports on the action taken on each input file: whether it
Xwas split (and how many parts), whether it was uuencoded, whether all files
Xhave been tar'red into a single one, the archive this file was archived under,
Xand the directory the output file(s) (if any) has/have been placed. It
Xalso reports when the input files are tarred.
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-n
XDo not split files when archiving them. The default is to split them with
Xeach part not larger than the specified size (see the -s option below).
X.TP
X-b
XInput files are binary; they are uuencoded before archived.
X.TP
X-B
XInput files are binary; they are neither uuencoded nor split.
X.TP
X-s size
XSpecify the maximum \fIsize\fP in kilobytes of each of the subparts 
X(default is 64).
X.TP
X-d dir
XSpecify the \fIdir\fPectory that the output files are to be placed; if
Xleft out, it defaults to the specified archive's directory
X(HOMEDIR/archives/listserver if the archive is left out as well).
XIf the directory (and all of its subdirectories, if any) does not exist,
Xit will be created.
X.TP
X-p password
XWhen a new archive is to be created, and that archive is to be private,
Xspecify the access password.
X.TP
X-D description
XPut \fIdescription\fP (most likely surrounded by single quotes) in the
XDIR file as explanatory comment about the archived file. This option does
Xnot make much sense when archiving more than one file at the same time.
X.TP
X-t file
XInput files are tar'red into \fIfile\fP which is then archived (the -b flag
Xis automatically turned on). Turning on the -B flag will prevent uuencoding
Xof the tar file. Note that whatever the path to \fIfile\fP may
Xbe, it will be moved to the specified directory as specified by the -d flag.
X.TP
X-r
XRemove the specified file(s) from the specified archive (see below), or the
Xdefault one; it has higher priority than all of the options above.
X.TP
X-a archive | path-to-archive
XSpecify the \fIarchive\fP that the input files will be archived under (default
Xis listserver). Archives on different levels of the hierarchy may have the
Xsame name and in this case a \fIpath-to-archive\fP may be specified to
Xdistinguish between them;
X\fIpath-to-archive\fP has the form \fIarchive[/archive[/archive...]]\fP
X(see also server(1)). If any of the archives in the path do not exist,
Xthey will be created with the necessary files.
X.PP
XFile names in the DIR file have to be unique and \fIfarch\fP will not
Xarchive duplicate files.
X.SH ARCHIVING\ A\ FILE
X\fIfarch\fP by default splits the input files if necessary
X(or the tar file, if any),
Xinto files of maximum size as specified. Each part
Xwill contain as many complete lines from the original input file as possible,
Xwithout exceeding the specified size. Binary files (including the tar file)
Xare uuencoded before they are archived unless the -B option is specified,
Xand all archived files are compressed, if possible.
X.SH ADDING\ A\ NEW\ ARCHIVE
X.TP
Xautomatically
XSimply specify the new archive as argument to the -a flag. All necessary DIR
Xand INDEX files as well as all required directories will be created, and
Xall parent archives will be updated.
X.TP
Xby hand
XStep 1
X.sp
XCreate a directory under HOMEDIR/archives or under an existing
Xarchive's directory (beware of the hierarchy structure), naming the
Xdirectory after the archive.
XThe archive's name should be unique among its siblings on that level in the
Xhierarchy (besides, mkdir will not let specify a name that already exists).
X.sp
XStep 2
X.sp
XEdit the master archive's index file (HOMEDIR/archives/listserver/INDEX) and
Xadd the new archive along with its path (and optional password -- see below)
Xat the proper place in the file, making sure you adhere to the
Xrules about hierarchy outined above. The first line of every INDEX
Xfile should be the archive itself. Also edit (add to) every ancestor archive's
XINDEX file, if the new archive is not the default, again making sure you adhere
Xto the same rules.
X.sp
XStep 3
X.sp
XCreate a new INDEX file in the new archive's directory and put an entry
Xfor itself.
X.sp
XStep 4
X.sp
XCreate an empty DIR file in the new archive's directory.
X.SH PRIVATE\ ARCHIVES
XPrivate archives are archives that require a password for obtaining
Xindices and/or files from them. To make an archive private, simply
Xappend a password (case does not matter) after its full path specification
Xin every INDEX file the archive is defined (its own plus all ancestors' --
Xthe same password has to be used in all of these files). Then, that 
Xpassword has
Xto be made known to all users who are to be granted access to this archive.
XNote that all files in the same private archive can be obtained with the
Xsame single password.
X.SH EXAMPLES
XArchive src/data and src/data2 under archive listserver (the default),
Xusing a maximum file size of 1K; the output file(s) will be placed in /tmp/tmp:
X.sp
X.nf
X.in +2
X% farch -s 1 -d /tmp/tmp src/data src/data2
X.fi
X.in -2
X.sp
XArchive /etc/hosts under archive unix (that is listserver/unix) -- the -n
Xflag is used to avoid writing split parts to /etc which is doomed to fail:
X.sp
X.nf
X.in +2
X% farch -n -a unix -d /etc -p private /etc/hosts
X.fi
X.in -2
X.sp
XArchive /etc/password under archive pub/unix (that is listserver/pub/unix):
X.sp
X.nf
X.in +2
X% farch -n -a pub/unix -d /etc /etc/passwd
X.fi
X.in -2
X.sp
XTar and archive all files in /usr/src to the default archive and place the
Xtar file under /tmp/tmp:
X.sp
X.nf
X.in +2
X% farch -t HOMEDIR/source -d /tmp/tmp /usr/src/*.c
X.fi
X.in -2
X.sp
XDescriptive messages about these files are added manually into the archive's
XDIR file. However, the -D option can be used to specify a string as follows:
X.sp
X.nf
X.in +2
X% farch -D 'UNIX ListServer system files' -t ulistserv.tar -n -d /usr/server /usr/server/*
X.SH UPGRADING
XIf you are upgrading from:
X.TP
X5.5
XThe -B, -p, -r and -D options are new; functionality is the same.
X.TP
X5.41 or less
XEvery DIR file should now include the size of each of the parts of every
Xfile, placed between the number of parts and the path name.
X.sp
XThe -a option has been extended to accept paths to archives.
X.SH RESTRICTIONS
X\-
XArchive names and input files must use only lower case characters of the
Xalphabet.
X.SH WARNINGS
X\-
XInput files that are to be tar'red should only include relative path names;
Xotherwise the end user may not be able to extract them.
X.sp
X\-
XThe tar file should not be any of the input files.
X.SH SEE\ ALSO
Xserver(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/farch.1-*-*
echo x - doc/iul.1
sed 's/^X//' >doc/iul.1 <<'*-*-END-of-doc/iul.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH iul 1 "IUL 1.0"
X.SH NAME
X\fBiul\fP \- Interactive UNIX ListServer client
X.SH SYNOPSIS
X\fBiul\fP [\fB-v\fP] [\fB-t timeout\fP] [\fB-b buffer-size\fP] \fBhost\fP [\fBport\fP]
X.SH DESCRIPTION
X\fIiul\fP is a client that connects to the specified \fIhost\fP's
XInteractive UNIX ListServer port (372) for a live session -- i.e. for
Xprocessing requests live. Another \fIport\fP
Xmay be specified if necessary. The \fIhost\fP may be a host name, or an 
XIP address.
X.PP
XUpon connection establishment, the protocol attempts to set the user's
Xprivileges during the session by requesting an email address and a password.
XThese are used to determine whether the user is to be granted system,
Xowner, subscriber or casual user privileges. If the user provides
Xhis owner email address and his list's password, he will be granted
Xowner privileges. If he provides an email address that he uses for subscription
Xto a list, along with the password he uses for that list, he will be granted
Xsubscriber privileges. The system provides a brief listing of all valid
Xrequests the user may issue during the session. If no email address is
Xprovided, or no matches are found, the user is restricted to a few
Xrequests.
X.PP
XRequests for remote lists are serviced by attempting to connect
Xto the IUL server(s) (if any) of the system(s) that handle those lists.
X.PP
XThe IUL server recognizes the following special requests:
X.TP
Xquit/exit
XEnd the session.
X.TP
X?/privileges
XGet a brief listing of all valid requests.
X.TP
Xtimeleft
XPrints the remaining time in seconds.
X.TP
X&<new-line>
XInput continues on the next line.
X.TP
Xbinary
XSwitch to binary mode when transferring files.
X.TP
Xascii
XSwitch to ASCII mode when transferring files.
X.TP
X< filename
XInput is taken from the specified file. Each line will be interpreted as
Xa separate request, unless the file is specified in conjunction with a
X\fIput\fP request. If the '<' is to be used literally it
Xmust be escaped with '\\' or enclosed in quotes.
X.TP
X> filename
XRedirect the reply to the request to the specified file. Error messages
X(such as rejections due to invalid requests, etc.) are not redirected.
XWhen a file is downloaded via a \fIget\fP request, this will override the
Xfile name that will be saved under. If the '>' is to be used literally it
Xmust be escaped with '\\', or enclosed in quotes.
X.TP
X>> filename
XSame as above, but the reply is appended to the specified file.
X.TP
X| prog [args]
XThe output of the request is piped to \fIprog\fP; this is similar to a UNIX
Xpipe. \fIprog\fP may be any valid UNIX command, including other pipes, file
Xredirections, etc. Since '<' has higher precedence in this context, you
Xshould escape any '<' characters intended to be used by the pipe, otherwise
Xthe system will assume you are feeding it batched requests. Quotes may also
Xbe used to protect these characters.
X.SH OPTIONS
XThe following options are recognized:
X.TP
X-v
XTurn verbose mode on; the server reply codes are echoed along with
Xpredetermined messages.
X.TP
X-t timeout
XThe default time out for a server response is 180 seconds; to reset
Xuse the -t flag. This timeout should not be confused with the
Xremaining time of a connection.
X.TP
X-b buffer-size
XThe default socket buffer size is 8K; to reset use the -b flag
X(the argument specifies kilobytes).
X
X.SH USER\ PRIVILEGES
XCasual users may only issue help, information, recipients and statistics
Xfor nonprivate lists, lists, index, get, search and release requests.
XSubscriber privileges also include the set, run, unsubscribe and which requests.
XOwners may, in addition, issue all of their administrative requests.
X.PP
XIssue a 'help live' request when you first connect to an IUL server.
X.SH RESTRICTIONS
XThe connection duration is limited to a server-imposed limit.
XAfter that, a connection may be broken by the server as necessary.
XA connection will not be broken during a transfer.
X.SH EXAMPLES
X.nf
Xrequest> put ermis ermis1 subscribers </usr/server/backup/lists/ERMIS/subs >out
X.sp
Xrequest> get listserver example.dat 1 3 >> out
X.sp
Xrequest> get listserver example.dat
X.sp
Xrequest> index iul > IUL
X.sp
Xrequest> < /tmp/batched.requests
X.sp
Xrequest> index | more
X.sp
Xrequest> lists | cut -d ' ' -f1,2 | more
X.sp
Xrequest> search iul "\\>\\> out"
X.sp
X.fi
XIn this last example, we escape '>' to protect it from being interpreted
Xas a regular expression separator, and enclose the whole pattern in quotes
Xto protect the '>>' from being interpreted as the append operator. The same
Xrequest could have been written as:
X.nf
X.sp
Xrequest> search iul \\\\>\\\\> out
X.fi
X.SH FILES
X.TP
Xiul.c, iul.h
XSource code for the client. See iul.c for compile options.
X.TP
Xiulp.h
XDefinition of the Interactive UNIX ListServer Protocol.
X.TP
Xmakefile
Xmakefile to build \fIiul\fP. \fIiul\fP is written with BSD-style signal
Xhandling, therefore on some hosts (like IBM AIX) you will have to link with
XBSD versions of signal(2), socket(3N), and fcntl(2). Platforms that
Xhave the <sys/select.h> and <ulimit.h> header files should compile
Xwith -DHAVE_SELECT_H and/or -DHAVE_ULIMIT_H. SCO ports should compile with
X-Dsco. Always link with libraries that provide DNS support (resolver linked
Xin).
X.SH BUGS
XPlease report any bugs or enhancements to tasos@cs.bu.edu
*-*-END-of-doc/iul.1-*-*
echo x - doc/list.1
sed 's/^X//' >doc/list.1 <<'*-*-END-of-doc/list.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH list 1 "UNIX ListServer"
X.SH NAME
X\fBlist\fP \- process a specified UNIX ListServer mailing list
X.SH SYNOPSIS
X\fBlist -L\fP\ <\fBLIST_ALIAS\fP> [\fB-1\fP] [\fB-e\fP] [\fB-s\fP] [\fB-p\fP] [\fB-P\fP] [\fB-m\ #\fP] [\fB-f\fP] [\fB-r\fP] [\fB-M\fP] [\fB-d\fP] [\fB-i\fP <\fBaddress\fP>] [\fB-v\fP] [\fB-D\fP]
X.SH DESCRIPTION:
X\fIlist\fP distributes the message(s) sent to \fIlist_alias@your-domain\fP;
Xthe file ".ignored" in the list's subdirectory is used to filter out any
Xunwanted messages (see below).
X.PP
XMessages from mailer daemons are forwarded to the list's owner and screened
Xlooking for delivery errors (in which case appropriate action is taken --
Xusers are either immediately removed from the list, or after a series of
Xdelivery errors within a certain time frame -- entries removed from the
Xsystem's files are placed into HOMEDIR/lists/*/removed.users and
XHOMEDIR/lists/*/removed.alias files),
Xwhereas messages
Xfrom non-subscribers are returned to the original senders (the -f flag
Xoverrides this). Messages whose first line looks like a ListServer request
Xare bounced back to the sender.
X.PP
XMessages from news groups are distributed only to local
Xsubscribers and peer lists. Messages from peers are distributed to local
Xsubscribers and possibly posted to news groups. Finally, messages from
Xlocal subscribers are either distributed locally, copies are sent to all peers,
Xand are possibly posted to news groups, or are forwarded to the list's
Xowner for screening if the list is moderated.
X.PP
XAll distributed messages 
X(i.e. legitimate messages from subscribers, peers and news groups -- not
Xrejected, ignored or error messages)
Xare automatically archived in HOMEDIR/lists/*/archive. In contrast,
Xthe files HOMEDIR/lists/*/mbox contain all messages sent to these
Xlists (including, error, rejected and ignored messages).
X.PP
XEmail from one or more subscribers may be selectively distributed to an
Xalternate list of recipients, by way of restricted mail (see below),
Xin which case mail will not be distributed to the regular subscribers.
X.PP
XWhen the system is for some reason aborted while making a delivery, a
Xbuilt-in mechanism allows it to resume from the point it left off.
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-L LIST_ALIAS
XProcess any messages sent to this \fILIST_ALIAS\fP -- \fILIST_ALIAS\fP should
Xbe in capital letters.
X.TP
X-1
XExecute only once; process the mailing list and return control to
X\fIserverd\fP(1); any new messages that may have arrived in the meantime will
Xbe processed at a later time. Without this option, \fIlist\fP will be
Xlistening for messages for the specified list for ever. \fIserverd\fP(1)
Xuses this option by default when spawning \fIlist\fP.
X.TP
X-e
XEcho reports to the screen; it has no effect if the system has been compiled
Xwith -DSYSLOG.
X.TP
X-s
XBy default, only subscribers can send messages to a list. This option
Xturns off subscription checking.
X.TP
X-p
XBy default, replies to messages posted to news groups go to the list; 
Xthis option forces replies to be forwarded to the original author.
X.TP
X-P
XBy default, replies to messages sent to subscribers and peers go to the
Xlist; this option forces replies to be forwarded to the original author.
X.TP
X-m number
XNormally, each outgoing message has one recipient. This flag switches to
Xmulti-recipient outgoing messages and specifies the \fInumber\fP of recipients
Xto be included in these messages.
X.TP
X-f
XForward any messages from non-subscribers to the list's owner. By default,
Xthey are returned to the sender.
X.TP
X-r
XRestricted mail: \fIlist\fP will look at the ".restricted" file (see
Xbelow) to get the name of the alternate recipients file. If the sender
Xis listed in that ".restricted" file, his messages will be distributed
Xto users listed in the alternate recipients file.
X.TP
X-M
XThe list is moderated;
Xall incoming messages not from the owner(s) are forwarded to the
Xlist's primary owner for review and editing. The owner then sends back the
Xones that are approved for posting (for an alternate scheme see the
XMODERATED\ LISTS section below).
X.TP
X-d
XForce a digest to be distributed. This flag is internally used by
X\fIserverd\fP(1) when digest time has been reached for this list.
X.TP
X-i address
XSend a partial digest to \fIaddress\fP (what has accumulated so far).
XThis flag is internally used by
X\fIlistserv\fP(1) when the user identified by \fIaddress\fP changes his
X\fImail\fP mode from \fIdigest\fP to something else. Note that when 
Xthe time arrives for the current digest to be distributed, this
Xuser will get duplicate messages.
X.TP
X-v
XPrint version information.
X.TP
X-D
XTurns debugging on. When the \fIsystem\fP mailmethod is used (see server(1)), a
Xcopy of the last SMTP transaction can be found in the files
XHOMEDIR/sent and HOMEDIR/received.
X.SH ADDING\ A\ NEW\ LIST
XTo add a new list, first shut the system down by executing \fIstart\fP(1):
X.sp
X.in +2
X\fI% start -k\fP
X.in -2
X.sp
XThen edit the \fIconfig\fP file and add a line defining the
Xnew list -- you may wish to add a comment and/or disable a few requests also.
XA new alias has to be set up in /etc/aliases, /usr/lib/aliases
Xor /usr/ucblib/aliases of the following form:
X.nf
X.sp
Xlist_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f"
X.sp
X.fi
XNote that \fIlist_alias\fP is all lower case and \fILIST_ALIAS\fP is all
Xupper case. In this case \fIcatmail\fP(1) appends incoming mail to the list's
Xmail file (HOMEDIR/lists/LIST_ALIAS/mail).
XAlso keep in mind the case of reformatting messages as described in
X\fIcatmail\fP(1).
X.PP
XFinally, restart the system. At this point, the file ".ignored" in the
XHOMEDIR/lists/LIST_ALIAS directory may be edited to add more
Xunwanted senders. You should also edit the files ".welcome" and ".info"
Xin the list's subdirectory; the former is included in the return message
Xfor a new subscription request for that list, and the latter
Xis included in the return message for an \fIinformation\fP request for that
Xlist (see \fIlistserv\fP(1)). These files may be simple text files, or shell
Xscripts (see the HELP section in server(1)).
X.SH ADDING\ A\ PEER\ LIST
XPeer lists are mailing lists that happen to be subscribers to one
Xor more of your own mailing lists; they handle local distribution of
Xmessages just like you do at your own site. Peer lists can be mutual
Xsubscribers, so that a message originating in a peer list gets distributed
Xover there locally, and a copy is sent to the other for local distribution,
Xand vice versa. An automatic mechanism is provided for avoiding loops.
XA peer list can be added to
Xa local mailing list by using the script \fIpeer\fP:
X.sp
Xpeer\ <LIST_ALIAS>\ <remote\ alias>\ <peer\ address>\ <remote\ ListServer\ address>
X.sp
Xwhere \fILIST_ALIAS\fP is a local list alias in capital letters, 
X\fIremote\ alias\fP is the peer's alias on the remote machine,
X\fIpeer\ address\fP is taken
Xfrom the first line of the header of a test message sent to your host
Xby the peer 
X(the first line may be something like: "From peer@other-domain" -- see also
Xthe discussion about aliases below), and
X\fIremote\ ListServer\ address\fP is the full email address of the
Xremote request-handler.
X.PP
XHere is an example for
Xestablishing connection between two peer lists a@domain1 and b@domain2:
XThe \fImanager\fP at domain1 issues the following command:
X.sp
X.nf
Xpeer a b b@domain2 listserver@hdomain2
X.fi
X.sp
XThe \fImanager\fP at domain2 issues the following command:
X.sp
X.nf
Xpeer b a a@domain1 listserver@domain1
X.fi
X.sp
XOnce these two commands are issued the connection is automatically
Xset up.
XPeer lists should make sure that only one of them posts
Xto the same news group(s), and that only one of them receives articles
Xfrom the same news group(s)/gateway(s).
XLists handled by the same server cannot be mutual
Xpeers. Finally, peer lists should not be regular subscribers (the
X\fIpeer\fP script places them in a file called ".peers").
X.SH NEWS\ GROUPS\ AND\ GATEWAYS
XA mailing list may be linked with one or more news groups (or gateways) 
Xfrom which
Xit may receive messages for local distribution, and/or send messages to
Xthe newsgroup(s) for posting \-\- in this case only messages from
Xregular subscribers and peers are sent for posting, i.e. no news messages
Xwill be posted to any news groups (or gateways). 
XA news group or gateway is linked using the script \fInews\fP:
X.sp
X.ce
Xnews\ <LIST_ALIAS>\ <news\ group>\ <email\ address>\ <mode>
X.sp
Xwhere \fILIST_ALIAS\fP is a local list alias (in capital letters) 
Xthat is being linked to the news group, \fInews\ group\fP is the name
Xof the news group (used only when posting) e.g. misc.test,
X\fIemail\ address\fP is the address of the backbone or moderator of 
Xthe news group, or the gateway, and it is taken
Xfrom the first line of the header of a test message sent to your host
Xby the group (the first line may be something like: 
X"From gateway@foo ..." -- see also the discussion about aliases below);
X\fImode\fP is one of the following:
X.TP
Xreceive
XThe list will only be receiving messages from this news group and never
Xpost to it. This allows access to the group (or gateway)
Xto send articles to the list.
X.TP
Xsend_receive
XThe list may be receiving messages from this news group, and it will post
Xto it any messages from regular subscribers and peer lists (messages from
Xnews groups are never posted to other news groups). This also
Xallows access to the group (or gateway) to send articles to the list.
X.PP
XOf course, the news group's
Xcaretaker has to be notified of the list's address so that articles will indeed
Xbe sent to it. Finally, news groups should not be regular subscribers (the
X\fInews\fP script places them in a file called ".news"). 
X.PP
XIf the config option \fIpost_mail\fP is used,
Xthe system will use \fIinews\fP for posting, and it assumes that the
Xpath to it is /usr/lib/news/inews, so make sure that \fIinews\fP resides there,
Xor a link exists to it. In this case, the group's name is used for posting
X(e.g. misc.test). If instead \fIgate_mail\fP is defined, messages will
Xbe sent via email to news gateways (using the \fIemail\ address\fP)
X-- the \fInews\ group\fP name has no significance in this case.
X.SH MODERATED\ LISTS
XThe system supports two schemes for moderating lists. The first scheme uses
Xthe -M flag to \fIlist\fP(1), and in this case messages not from the
Xlist's owner(s) are forwarded to the list's primary owner for review;
Xthe owner then sends back the approved (and possibly edited) ones.
X.PP
XThe second scheme uses the -m flag to \fIcatmail\fP(1). In this case, the
Xlist's alias is changed slightly in the aliases file:
X.nf
X.sp
Xlist_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f -m"
X.sp
X.fi
X\fIlist\fP(1) distributes messages from a file called \fImail\fP; by having
Xincoming messages redirected to the file \fImoderated\fP (the effect of the
X-m flag to \fIcatmail\fP(1)), the list's owner may \fIapprove\fP or
X\fIdiscard\fP any number of these messages (see \fIlistserv\fP(1)).
XWhenever a new
Xmail message arrives for a moderated list, a copy is sent to the list's
Xowner with instructions for approving or discarding it. These instructions
Xinclude a unique tag number for identifying that message. When a message
Xis approved, it is transferred to the \fImail\fP file for later delivery,
Xand when discarded, it is simply removed from the
X\fImoderated\fP file. This scheme cuts down on mail traffic.
X.SH RESTRICTED\ MAIL
XWhen the -r flag is used with \fIlist\fP(1), for every message received its
Xsender is checked against a list of "restricted" email addresses, in the
Xfile ".restricted" in the list's directory -- a
Xsubset of the ".subscribers" file. If a match is found, mail is
Xforwarded to the people listed in the file following this email
Xaddress. If no match is found, the message is distributed to the
Xregular subscribers. Note that the alternate recipients file should
Xbe in the same format as the ".subscribers".
X.SH .SUBSCRIBERS
XThe format is as follows:
X.PP
XOne entry per line; each entry is the full email address of the subscriber
Xas it appears in the "From " field, followed by the word "ACK" (in which
Xcase his/her message will be sent back to him/her as an acknowledgement),
X"NOACK" (the opposite), "POSTPONE" (no mail will be sent until the
Xuser changes mode again),
Xor "DIGEST" (digests are periodically sent), followed by a string that
Xplays the role of the user's password, followed by either "YES" or "NO"
Xwhich are the values of the conceal attribute, followed by the subscriber's
Xname. See also \fIlistserv\fP(1).
X.SH .RESTRICTED
XThe format is as follows:
X.PP
XOne entry per line; each entry is the full email address of the
Xsubscriber, followed by a file name where email addresses of recipients
Xare listed (just like in the ".subscribers" file). Example:
X.sp
X.ce
Xjdoe@foo.bar.com  HOMEDIR/lists/LIST_ALIAS/.recipients
X.sp
XIf the recipient file given is the word "NONE", then no one will receive
Xany messages.
X.SH .ALIASES
XIt is possible that a subscriber's/peer's/news group's/gateway's email
Xmay arrive using a different path than registered, which may raise
Xsubscription issues. To work around this,
Xeach list provides a ".aliases" file in its subdirectory, which may contain
Xalternate email addresses to be used when checking for subscription. The
Xformat is one line per alias with the following information:
X.sp
X.ce
Xalias-address address-as-subscribed
X.sp
XPlease note that only the subscribed address is used for sending out
Xemail (see next section about regular expressions).
X.PP
XThe aliased address may be a regular expression with egrep(1) style syntax,
Xin which case
Xthe following characters have special meanings: '~', if leading the
Xregular expression it reverses its meaning; '|' and '&' separate multiple
Xregular expressions (logical OR and AND); '<' '>' group regular expressions
X(we preserve the meaning of the parentheses from ed(1), and remove the
Xmeaning of < and > from ed(1) since in the ListServer context they are either
Xthe default, or inappropriate).
XThese can be used literally by escaping them
Xwith '\\'. In addition, the following characters should be defined
Xin matched pairs: (), <>, [], "". For example:
X.sp
X.ce
Xjdoe@.*\\.bar\\.com jdoe@foo.bar.com
X.sp
Xwill enable this user to send messages from any machine in his local
Xnetwork and receive replies at foo.bar.com -- keep in mind that a '.'
Xmatches exactly one character, and '.*' matches zero or more characters.
XIn a more complicated example:
X.sp
X.ce
X~jdoe@cc.*|jdoe@....*\\.bar\\.com jdoe@foo.bar.com
X.sp
Xwill match if jdoe sends a message from a machine whose name does not start
Xwith 'cc', or from a machine whose name has at least 3 characters.
X.PP
XIf certain parts of the regular expression are parenthesized, then the
Xstrings that matched the subexpressions can be used in
Xthe 'address-as-subscribed' to form new return addresses; these matched
Xstrings are accessed by \fI\\n\fP where \fIn\fP is a digit between 1 and
X9. For example, if the sender is:
X.sp
X.ce
XGATE!HOP!USER@UUCP.SOME.COM
X.sp
Xand the entry in the ".aliases" file reads:
X.sp
X.ce
X[^!@]*!([^!@.]*)!([^!@]*)@.*	\\2@\\1.UUCP
X.sp
Xthen what will be returned as 'address-as-subscribed' is:
X.sp
X.ce
XUSER@HOP.UUCP
X.sp
XThis way you can map addresses coming in via various gateways to steady ones
X(\\2 refers to the second set of parentheses, and \\1 to the first).
XManagers may edit HOMEDIR/src/regex.c to test regular expression
Xbehavior, introduce ls(1) style meanings to the * and ? wild characters,
Xundefine ed(1) special characters, and enforce strict egrep(1) syntax (remove
Xthe meanings of ~ & < and >).
X.PP
XRegular expressions should be used with caution for obvious reasons.
X.PP
XIf someone is experiencing subscription
Xproblems, you may wish to add their alternate email address(es) in this
Xfile. This includes regular subscribers, news groups, peers and gateways.
XIf the sender is also a restricted subscriber, do not forget to put
Xanother entry in ".restricted" with the new alternate address. Any
Xnumber of aliases may be defined for each individual address.
X.PP
XIn addition, a ".aliases" file is also provided for Listserver
Xrequests and is under HOMEDIR. The same syntax is used, but each
Xentry has a slightly different meaning:
X.sp
X.ce
Xaddress-as-arrived address-used-for-reply
X.sp
XIn this case, \fIaddress-used-for-reply\fP will be used to reply to all
Xrequests sent in by \fIaddress-as-arrived\fP. A user may have any
Xnumber of aliases, but only the first one matching
X\fIaddress-as-arrived\fP will be used. Like before, the first argument may
Xbe a regular expression and the second may use the matches in the first.
X.sp
XFor each new list, the system puts the following default alias in its
X".aliases" file:
X.sp
X.ce
X^@.*:(.*)@(.*) \\1@\\2
X.sp
XThis removes source routing.
X.SH .IGNORED
XAs described before, the system's home directory as well as every list's
Xsubdirectory contains a ".ignored" file which is used to filter out messages
Xsent by certain users. The default file contains entries for server,
Xbin, and sys; you may wish to add an entry for every list alias that is
Xdefined on your system. A list's ".ignored" file also contains an
Xentry of its alias and full email address, as well as the server account's
Xfull email address.
X.PP
XEntries in this file may also be regular expressions as explained in the
Xprevious section. For example, to restrict requests (and postings)
Xto .com and .edu domain addresses, one may add:
X.sp
X.ce
X~<.*\\.com|.*\\.edu>
X.sp
XNotice, it is incorrect to list them as follows:
X.sp
X.ce
X~.*\\.com
X.ce
X~.*\\.edu
X.sp
Xas anything not from the .com domain matches the first regular expression,
Xand therefore will be ignored.
XTo refuse access to certain user names and certain sites one may include:
X.sp
X.ce
X \.*\\.bar\\.com|jdoe@.*
X.sp
XThe system's manager should use extra caution when adding regular expressions
Xto the system's ".ignored" file, because a simple '.*' prohibits anyone from
Xusing its services.
X.SH SEE\ ALSO
Xcatmail(1), listserv(1), server(1), serverd(1), start(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/list.1-*-*
echo x - doc/listserv.1
sed 's/^X//' >doc/listserv.1 <<'*-*-END-of-doc/listserv.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH listserv 1 "UNIX ListServer"
X.SH NAME
X\fBlistserv\fP \- process requests sent to the UNIX ListServer request handler
X.SH SYNOPSIS
X\fBlistserv\fP [\fB-1\fP] [\fB-e\fP] [\fB-i\fP] [\fB-n\fP] {[\fB-a\fP\ <\fBLIST_ALIAS\fP>]}* {[\fB-r\fP\ <\fBreq\fP>]}* {[\fB-d\fP\ <\fBreq\fP>]}* {[\fB-b\fP\ <\fBreq\fP>]}* [\fB-B\fP] [\fB-D\fP]
X.SH DESCRIPTION
X\fIlistserv\fP processes user requests sent to \fIlistserver@your-domain\fP,
Xforwards requests about known remote lists, and processes list-owner
Xadministrative requests (see also the LIST\ OWNERS section below).
XEach request should appear in a separate line with any possible arguments.
XThe file
X".ignored" is used in the system's home directory to filter out unwanted
Xsenders. If the system is not instructed to ignore invalid requests
X(see the CONFIG section in server(1)), the sender is notified of the first
Xinvalid request; all subsequent requests are ignored.
XFor each successfully completed request,
Xa confirmation is sent back to the sender.
X.PP
X\fIlistserv\fP stops reading
Xrequests when it encounters the string "--" in a line by itself, which
Xon most systems signifies the start of the .signature message, or at the
Xfirst occurence of a thankful request. \fIlistserv\fP reads the \fIconfig\fP
Xfile (see server(1)).
X.PP
XSubscriptions may be
Xmanager-approved (private lists), files and archive indices may be password
Xprotected (private archives), and requests may be placed in a batch queue.
XA single request may span multiple lines if each part ends with
X\fI&\\n\fP.
X.SH USER\ REQUESTS
XThe following user requests are recognized (requests may be abbreviated):
X.TP
X\fIhelp\fP\ [topic]
XSend a help message on all valid requests or the selected topic (possibly
Xa request) only.
X.TP
X\fIset\fP\ list\ [option arg(s)]
XWithout the optional arguments, return the current values for all options
Xset for \fIlist\fP; otherwise, 
Xset subscriber preferences for \fIlist\fP.
X.sp
X\fIoption\fP can be:
X.in +2
X.sp
X\fImail\fP: set mail preferences.
X.sp
X.in +2
X\fIarg\fP has to be one of the following:
X.sp
X.in +2
X\fIack\fP: send a copy of the current message to the original sender.
X.sp
X\fInoack\fP: do not send a copy of the current message to the original sender.
XThis is the default for newly subscribed users.
X.sp
X\fIpostpone\fP: do not send any messages to the particular subscriber until
Xhe changes status again.
X.sp
X\fIdigest\fP: do not send individual messages to the particular
Xsubscriber. Instead, store messages in a digest and send it when the
Xdigest exceeds a specified number of lines, or when a specified amount
Xof time has passed since the last digest was sent.
X.sp
XIf the mail mode is changed from \fIdigest\fP to anything else,
Xall messages currently stored for the next digest will be sent to the
Xsubscriber at once, in digest format.
X.in -2
X.in -2
X.sp
X\fIpassword\fP: change the password (used to establish
Xsubscriber privileges when connecting to ListServer for a "live" session and
Xfor the option below).
X.sp
X.in +2
X\fIargs\fP must be: old-password new-password.
X.in -2
X.sp
X\fIaddress\fP: set the address the user is subscribed with (if allowed for this
Xlist).
X.sp
X.in +2
X\fIargs\fP must be: current-password new-address.
X.sp
X.in -2
X\fIconceal\fP: set the user's visibility.
X.sp
X.in +2
X\fIarg\fP must be one of the following:
X.sp
X.in +2
X\fIyes\fP: the user is not listed in \fIrecipients\fP and \fIstatistics\fP
Xrequests.
X.sp
X\fIno\fP: the user's email address and name are made public.
X.in -2
X.in -2
X.in -2
X.TP
X\fIsubscribe\fP\ list\ full_name
XSubscribe the sender to \fIlist\fP (note, his email address is used for
Xsubscription, not his \fIfull_name\fP). A password is assigned by the
Xsystem at that time and is included in the reply message to the new
Xsubscriber. The password is to be used when connecting to the interactive
Xpart of the system for identification purposes, and for changing the
Xsubscription address if allowed for this list.
X.TP
X\fIwhich\fP
XGet a list of local mailing lists to which the sender has subscribed.
X.TP
X\fIunsubscribe\fP\ list
XRemove the sender from the specified \fIlist\fP.
X.TP
X\fIsignoff\fP\ list
XAlias of the unsubscribe request.
X.TP
X\fIrecipients\fP\ list
XGet a list of all subscribers of \fIlist\fP. The request is also forwarded
Xto all peer lists, and the servers handling them will respond accordingly.
XThe user is notified when this request is being forwarded. If a list
Xis private, only members may issue this request.
X.TP
X\fIreview\fP\ list
XAlias of the recipients request.
X.TP
X\fIinformation\fP\ list
XGet information about a particular \fIlist\fP.
X.TP
X\fIstatistics\fP\ list\ {[subscriber\ email\ address(es)]\ |\ [-all]}
XObtain a count of messages sent per subscriber to the specified \fIlist\fP,
Xor by those subscribers given as argument only (wild characters are
Xsupported). If \fI-all\fP is specified, then statistics are compiled for all
Xusers (currently subscribed or not) who have posted to the list in the past.
XThe request is also forwarded to all peer lists and the
Xservers handling them will respond accordingly.
XThe user is notified when this request is being forwarded.
XIf a list is private, only members may issue this request.
X.TP
X\fIrun\fP\ list\ [password\ cmd\ [args]]
XWithout the optional arguments, just list all commands that may be executed
Xby subscribers of this \fIlist\fP. Otherwise, run \fIcmd\fP with the
Xoptional \fIargs\fP, if the correct \fIpassword\fP is provided. The reply
Xwill contain the output from stdout and/or stderr.
X.TP
X\fIlists\fP
XObtain a list of mailing list addresses that are serviced by this system,
Xwith a small description of their purpose. If remote lists (see below)
Xare also defined, their addresses and descriptive messages will also be
Xincluded.
X.TP
X\fIindex\fP\ [archive\ |\ path-to-archive]\ [/password]\ [-all]
XObtain an index of files in the specified \fIarchive\fP (or the master archive
Xif none specified) and all of its subarchives if the the \fI-all\fP
Xoption is specified. Certain archives may be
Xprivate, and these require a \fI/password\fP to be able to obtain their
Xindices. Archives on different places in the hierarchy may have the same
Xnames and they can be distinguished by specifying paths to them; 
X\fIpath-to-archive\fP has the form \fIarchive[/archive[/archive...]]\fP.
X\fIindex\fP requests always report the paths to the archives they list.
X.TP
X\fIget\fP\ <archive\ |\ path-to-archive>\ file\ [/password]\ [parts]
XGet the specified \fIfile\fP from the \fIarchive\fP given. The file 
Xmay have been
Xsplit into smaller parts due to its size, in which case each part will
Xbe sent in a different email message. If only certain \fIparts\fP are desired,
Xthey may be given as arguments (numbers, separated by spaces -- ranges
Xare not recognized). Certain archives may be private, in which case their
X\fI/password\fP has to be provided in order to get the desired files.
XArchives on different places in the hierarchy may have the same
Xnames and they can be distinguished by specifying paths to them;
X\fIpath-to-archive\fP has the form \fIarchive[/archive[/archive...]]\fP.
XIf the file is pure binary, it will be uuencoded first.
X.TP
X\fIsearch\fP\ <archive\ |\ path-to-archive>\ [/password]\ [-all]\ <pattern>
XSearch all files in the specified archive (and all of its subarchives if
X\fI-all\fP) and return lines that match the \fIpattern\fP.
XThe pattern can be an extended regular expression with egrep(1)-style syntax,
Xwith support for the following additional operators: '~', if leading the
Xregular expression it reverses its meaning; '|' and '&' separate multiple
Xregular expressions (logical OR and AND); '<' '>' group regular expressions
X(we preserve the meaning of the parentheses from ed(1), and remove the
Xmeaning of < and > from ed(1) since in the ListServer context they are either
Xthe default, or inappropriate).
XThese can be used literally by escaping them
Xwith '\\'. In addition, the following characters should be defined
Xin matched pairs: (), <>, []. \fIpattern\fP may be enclosed in single
Xor double quotes. Pattern matching is case insensitive.
X.TP
X\fIfax\fP\ <fax-no>\ <archive\ |\ path-to-archive>\ file\ [/password]\ [parts]
XSame as the \fIget\fP request except that the files are faxed to the
Xspecified number.
X.TP
X\fIrelease\fP
XGet information about the current release of this server system.
X.TP
X\fIexecute\fP\ password\ #command\ [arguments]
XIntended for the system's \fImanager\fP, this will execute the UNIX \fIcommand\fP
Xwith the optional \fIarguments\fP and send the output (if any) from stdout
Xand/or stderr to the sender. See the sample \fIconfig\fP file for
X\fIpassword\fP definition.
X.PP
XFor a list of the list administration requests that may be issued by list
Xowners, see the LIST\ OWNERS section below.
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-1
XExecute only once; process any requests and return control to
X\fIserverd\fP(1); any new messages that may have arrived in the meantime will
Xbe processed at a later time. Without this option, \fIlistserv\fP will be
Xlistening for requests for ever. \fIserverd\fP(1)
Xuses this option by default when spawning \fIlisterv\fP.
X.TP
X-e
XEcho reports to the screen; it has no effect if the system has been compiled
Xwith -DSYSLOG.
X.TP
X-i
XGo to interactive mode -- messages by \fIlistserv\fP are not mailed out
Xbut instead \fIserverd\fP(1) reads them during its interactive session. Do not
Xuse this option in the \fIconfig\fP file (see server(1)).
X.TP
X-n
XBy default, peer servers are notified upon \fIstatistics\fP and
X\fIrecipients\fP requests. The system uses a protocol for avoiding
Xloops as described in \fIserver(1)\fP.
XIf you detect loops with other servers, you should use this option to
Xturn off notification of peer servers.
X.TP
X-a LIST_ALIAS
XUsually, subscriptions are automatic. This option turns off automatic
Xsubscription to the specified list (\fILIST_ALIAS\fP should be in capital
Xletters), and makes this list private (members only may issue \fIrecipients\fP
Xand \fIstatistics\fP requests). The sender is notified of this effect,
Xand a message is sent
Xto the list's owner requesting his/her approval, with instructions for
Xplacing the subscription. Notice that the specified list has to be defined
Xbeforehand via a \fIlist\fP directive in the \fIconfig\fP file. This
Xoption may be repeated any number of times.
X.TP
X-c LIST_ALIAS
XConceal \fILIST_ALIAS\fP from \fIlists\fP requests.
X.TP
X-r request
XPlace a restriction on the specified server \fIrequest\fP as outlined above.
XIf the number of users on the system at the time the request is about
Xto be processed is above the limit
Xgiven in the \fIconfig\fP file (using the \fIrestriction\fP directive),
Xthe request is rejected \-\- meant for requests that may take a 
Xconsiderable amount
Xof resources such as the \fIstatistics\fP request \-\- this option may
Xbe repeated any number of times. List administration requests are not
Xsubject to these restrictions (see later on).
X.TP
X-d request
XDisable \fIrequest\fP, i.e. make it totally unknown to the server \-\- this
Xsupersedes any \fIdisable\fP directives for this request in the \fIconfig\fP
Xfile, i.e. this request will not be recognized for any list (see
X\fIserver(1)\fP).
XHowever, help is still available for that request. List administration
Xrequests are also subject to these restrictions (see later on). This
Xoption may be repeated any number of times.
X.TP
X-b request
XAll such \fIrequest\fPs will be batch-processed if they arrive between
Xthe hours specified in the \fIconfig\fP file.
XThis option may be repeated any number of times.
X.TP
X-B
XProcess the batch queue. This is done automatically by \fIserverd\fP(1) after
Xmidnight every day, or when the system is restarted, so no further action
Xneeds to be taken. Caution:
Xdo not place this option in the definition of \fIserver\fP in the
X\fIconfig\fP file. If you do so, only the batch queue
Xwill be processed (no new requests will be processed, ever).
XThe batch queue is processed once a day only, unless the system
Xis restarted repeatedly.
X.TP
X-D
XTurns debugging on. When the \fIsystem\fP mailmethod is used, a
Xcopy of the last SMTP transaction can be found in the files
XHOMEDIR/sent and HOMEDIR/received.
X.SH PEER\ SERVERS
XThe system supports the notion of remote lists. A ListServer may know of
Xremote lists served by remote servers. Yet users may send requests about
Xthese remote lists to this server, which will in turn forward
Xthem to the remote servers. The user is notified when a list is not local
Xand his/her request is about to be forwarded.
XA \fIlists\fP request will tell the user which remote lists are known 
Xto this server.
X.PP
XRemote lists should be made known
Xto this server only if they are served by a server of version 5.31 or higher.
XThis restriction is posed because of incompatible requests
Xacross various list server systems; for example, the meaning of an 
X\fIinformation\fP request is different on a BITNET Listserv. See the
Xdiscussion about the \fIconfig\fP file for setting up remote lists.
X.PP
XIn addition, \fIrecipients\fP and \fIstatistics\fP requests are
Xforwarded to the servers handling peer lists (the -n flag to \fIlistserv\fP
Xturns this feature off).
X.SH LIST\ OWNERS
XList owners are individuals responsible for list administration via
Xmail requests. Thus, list owners may be remotely located. Each list
Xhas to have at least one list owner. These owners may be different than
Xthe system's \fImanager\fP, and have special privileges: they may issue
Xrequests on users' behalf (add a user, remove a user, etc.) overriding
Xsystem restrictions set on regular users (these include disabled commands
Xas described above),
Xobtain reports about the lists they administer,
Xappend to the ".aliases" and ".ignored" files, change the welcoming
X(".welcome") and informative (".info") messages, as well as other system
Xfiles such as the aliases file (".aliases"), the ".ignored" file, the
Xsubscribers file (".subscribers"), the news file (".news") and the
Xpeers file (".peers"). In addition, they may moderate their lists and they
Xreceive various error messages pertaining to their lists.
XAll administrative requests are author authenticated and
Xpassword protected. Whenever a message cannot be author authenticated,
Xthe list's owner and \fImanager\fP are notified.
X.PP
XOn the other hand, list owners may not add restricted users; this
Xservice can be provided by contacting the system's \fImanager\fP.
X.PP
XList owners may also receive copies of user requests and/or error
Xmessages such as invalid postings, syntax errors on requests, etc.
XThese options are described in the following sections.
X.TP
XDefining list owners
XOnce a new mailing list is defined in the \fIconfig\fP file, the list's
Xowner address and access password are provided to the \fIlist\fP directive.
XOf course, this password should be made known to the owner; it
Xwill be needed for all administrative requests. Next, the owner's
Xaddress has to be registered in HOMEDIR/owners; the \fImanager\fP
Xsimply edits this file adding the owner's address along with the
Xlist's name (alias) he is assigned to, followed by any
Xpreferences (see below).
XMultiple owners for a list may be defined by adding their addresses to
Xthis file, and providing them with the list's password. However, only
Xthe primary owner's address (as defined by the \fIlist\fP directive) will
Xbe used as reference in correspondence and for system-error notifications,
Xand only the primary owner will be forwarded messages from his moderated list
Xfor approval.
XThe \fImanager\fP should also add his address (login name) to this file.
X.TP
XOwner preferences
XThe primary owner may wish to be copied on certain replies to user
Xrequests (such as subscribe), on error conditions (rejected
Xpostings, invalid requests, etc.), or on all cases. These preferences
Xare listed in the \fIowners\fP file on the line his address and list are
Xdefined. Valid preferences are:
X.sp
X.in +2
XCCSET: copy on \fIset\fP requests.
X.sp
XCCSUBSCRIBE: copy on \fIsubscribe\fP requests.
X.sp
XCCUNSUBSCRIBE: copy on \fIunsubscribe\fP requests.
X.sp
XCCRECIPIENTS: copy on \fIrecipients\fP requests.
X.sp
XCCINFORMATION: copy on \fIinformation\fP requests.
X.sp
XCCSTATISTICS: copy on \fIstatistics\fP requests.
X.sp
XCCRUN: copy on \fIrun\fP requests.
X.sp
XCCPRIVATE: copy on requests rejected because they are open only to
Xthe list's members.
X.sp
XCCERRORS: copy on various error conditions.
X.sp
XCCALL: all of the above.
X.in -2
X.sp
XOwner preferences are optional.
X.PP
XThe \fImanager\fP may define preferences
Xfor himself as well by using the keyword \fIserver\fP in place of a
Xlist alias. However, such preferences make sense only in the following
Xcases:
X.sp
X.in +2
XCCGET: copy on \fIget\fP requests.
X.sp
XCCINDEX: copy on \fIindex\fP requests.
X.sp
XCCLISTS: copy on \fIlists\fP requests.
X.sp
XCCRELEASE: copy on \fIrelease\fP requests.
X.sp
XCCHELP: copy on \fIhelp\fP requests.
X.sp
XCCERRORS: copy on various error conditions.
X.sp
XCCALL: all of the above.
X.in -2
X.sp
XManager preferences are optional.
X.TP
XOwner privileges
XWhenever an owner issues a user request on a user's behalf (see below),
Xall restrictions, including disabled commands, do not apply. All other
Xadministrative requests are subject to restrictions set by the \fImanager\fP.
XAll requests (user and administrative) are subject to batch-processing.
X.TP
XAdministrative requests
XThe following requests may be issued by a list's owner:
X.TP
X\fIsystem\fP\ list password\ user-address\ #user-request
XThis request overrides all system restrictions and executes
X\fIuser-request\fP on behalf of \fIuser-address\fP; this
Xaddress has to appear as listed in the ".subscribers" file, where applicable.
XThe most frequent use of the \fIsystem\fP request is to subscribe a user
Xto a private list. For example:
X.sp
X.in +2
Xsystem herc herc1 john@foo #subscribe herc Some Name
X.in -2
X.sp
XIf a \fIuser-request\fP refers to a list, this list has to be
X\fIlist\fP, so that a list's owner may not have privileges over another
Xlist's affairs. Note that all replies about \fIuser-request\fP are
Xforwarded to \fIuser-address\fP, not the owner; therefore, care has to
Xbe taken to avoid syntax errors. The \fIsystem\fP request is not subject
Xto restrictions, disabled requests, and private list subscription
Xverification (it is still subject to private list review as outlined 
Xabove, and batching). To remove a member from his list, the owner may issue the
Xfollowing request:
X.sp
X.in +2
Xsystem herc herc1 john@foo #unsubscribe herc
X.in -2
X.sp
XTo bypass restrictions and review his list, the owner may issue the
Xfollowing:
X.sp
X.in +2
Xsystem venus venus1 his-address #review venus
X.in -2
X.sp
XIn general, \fIuser-request\fP may be any of the recognized user requests
Xdescribed under \fIlistserv\fP. The pound sign is mandatory.
XThere is no help available to users for this request for security reasons.
X.TP
X\fIapprove\fP\ list\ password\ tag
XWhenever a new message arrives for a moderated list a copy is sent to the
Xlist's owner soliciting his approval -- proper instructions for approving
Xor discarding a message are included. This request
Xapproves the message identified by the \fItag\fP number for posting to
X\fIlist\fP. The tag number is
Xprovided to the list's owner by \fIlistserv\fP and is unique.
X.TP
X\fIdiscard\fP\ list\ password\ tag
XIn contrast to the above request, this discards the message identified
Xby \fItag\fP. Messages that are not approved or discarded remain in the
Xlist's \fImoderated\fP file (see \fIlist(1)\fP).
X.TP
X\fIreports\fP\ list\ password
XObtain all reports pertinent to \fIlist\fP; this will send two mail messages:
Xone with the current report (HOMEDIR/lists/ALIAS/.report.list), and
Xone with the previously archived ones (HOMEDIR/lists/ALIAS/.rep.list.acc);
Xsee the REPORTS section below. Once the ".rep.list.acc" file is sent,
Xit is shrunk in size, therefore the owner should make sure he keeps the
Xcopy he receives.
X.sp
XThis request has no effect if the system is using syslog(3) to generate
Xreports.
X.TP
X\fIedit\fP\ list\ password\ file
XObtain the specified \fIfile\fP for editing; candidate files are:
X.sp
X.in +2
X\fIaliases\fP: obtain the list's aliases file.
X.sp
X\fIignored\fP: obtain the list's list of unwelcome addresses.
X.sp
X\fIinfo\fP: obtain the list's informative message.
X.sp
X\fIsubscribers\fP: obtain the list's subscribers list.
X.sp
X\fIwelcome\fP: obtain the list's welcoming message.
X.sp
X\fInews\fP: obtain the list's list of newsgroup connections.
X.sp
X\fIpeers\fP: obtain the list's peers.
X.in -2
X.TP
X\fIput\fP\ list\ password\ keyword\ [args]
XThis enables the list's owner to append to the ".aliases" and ".ignored" files,
Xand replace his list's ".welcome", ".info", ".aliases", ".ignored",
X".subscribers", ".news" and ".peers" files, depending
Xon the \fIkeyword\fP. Valid \fIkeyword\fPs are:
X.in +2
X.sp
X\fIalias\fP: Add a user address alias to the list's and system's ".aliases"
Xfiles (see .ALIASES below). This requires the new address and the address
Xused for subscription as arguments:
X.sp
Xput <list> <password> alias <new-alias> <address-as-subscribed>
X.sp
XFor example:
X.sp
X.in +2
Xput venus venus1 alias foo!john john@foo
X.in -2
X.sp
X\fIignore\fP: Add a user address to the list's ".ignore" file only. Of
Xcourse this address has to be provided as argument:
X.sp
Xput <list> <password> ignore <address-as-subscribed-or-aliased>
X.sp
XFor example:
X.sp
X.in +2
X.nf
Xput ermis ermis1 ignore jack@foo
Xput ermis ermis1 ignore foo!jack
X.fi
X.in -2
X.sp
X.nf
X\fIwelcome\fP,
X\fIinfo\fP,
X\fIaliases\fP,
X\fIignored\fP,
X\fIsubscribers\fP,
X\fInews\fP,
X.fi
X\fIpeers\fP: Create a new system file.
X.sp
XFor example:
X.sp
X.in +2
X.nf
Xput <list> <password> subscribers
Xtasos ACK PASSWORD NO Tasos Kotsikonas
Xjohn NOACK PASS1 NO John Doe
X.fi
X.in -2
X.sp
XNo arguments are needed. The text that is to
Xgo to the corresponding file starts at the line following this request
Xand spans till the end of the mail message. Thus, no more requests can
Xbe made in the same mail message -- they are treated as regular text;
Xsignature lines also signify the end of text, provided they start with
X"--" in a single line.
X.in -2
X.sp
XA confirmation is sent to the owner once a \fIput\fP request is successfully
Xprocessed.
X.SH OWNERS
XThe format of the \fIowners\fP file is as follows:
X.sp
XOne entry per line; each entry is the full email address of a list's
Xowner, followed by the list alias he owns, followed by any optional
Xpreferences. If the keyword \fIserver\fP is specified in place of the
Xlist alias, then preferences are defined for the \fImanager\fP.
X.SH SEE\ ALSO
Xcatmail(1), farch(1), list(1), queue(1), server(1), serverd(1), start(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/listserv.1-*-*
echo x - doc/queue.1
sed 's/^X//' >doc/queue.1 <<'*-*-END-of-doc/queue.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH queue 1 "UNIX ListServer"
X.SH NAME
X\fBqueued\fP \- UNIX ListServer mail queue daemon
X.SH SYNOPSIS
X\fBqueued\fP <\fBfrequency\fP>
X.SH NAME
X\fBpqueue\fP \- process the specified mail queue files
X.SH SYNOPSIS
X\fBpqueue\fP [\fB-e\fP] [\fB-D\fP] <\fBfiles\fP>
X.SH OVERVIEW
XThis part of the UNIX ListServer system can be employed only when using the
X\fIsystem\fP mailmethod (see \fIserver\fP(1)).
XMessages and replies to requests not
Xdelivered due to network problems are queued by the system in the
Xdirectory HOMEDIR/mqueue. This part of the system attempts periodic
Xredelivery of these files. If problems still persist, files (messages)
Xthat cannot be delivered are requeued.
X.SH DESCRIPTION:\ queued
XThis is the daemon that looks for files in the mail queue. It uses
X\fIpqueue\fP for redelivery. The queue is checked every \fIfrequency\fP
Xseconds. Whenever an error occurs with \fIpqueue\fP, \fIqueued\fP sends
Xa mail message to \fImanager\fP (as defined in the \fIconfig\fP file)
Xand aborts.
X.PP
X\fIqueued\fP is not spawned by \fIstart\fP(1) in order to reduce the number of
Xprocesses running, since the probability of messages being queued is
Xvery low. Instead, it should be started manually whenever there are
Xfiles in the mail queue directory.
X.SH DESCRIPTION:\ pqueue
XThe \fIfiles\fP given as arguments are redelivered. If any of these
Xcannot be redelivered, they are requeued and will be processed in the next
Xrun. \fIpqueue\fP reports to the file HOMEDIR/.report.pqueue
X.PP
XThe following command line options are recognized:
X.TP
X-e
XEcho reports to the screen; it has no effect if the system has been compiled
Xwith -DSYSLOG.
X.TP
X-D
XTurns debugging on; a copy of the last SMTP transaction can be found in 
Xthe files HOMEDIR/sent and HOMEDIR/received. Warning: these files
Xare also used by \fIlist\fP(1) and \fIlistserv\fP(1) when they have their
Xdebug mode on, so use caution.
X.SH NOTE
XThis mail queueing system should not be confused with the one implemented
Xby sendmail(1).
X.SH SEE\ ALSO
Xserver(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/queue.1-*-*
echo x - doc/server.1
sed 's/^X//' >doc/server.1 <<'*-*-END-of-doc/server.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH server 1 "UNIX ListServer"
X.SH NAME
XUNIX ListServer version 6.0
X.SH OVERVIEW
XThis is a system that implements various mailing lists with one
Xlist server. It is automated, and obliterates the need for user intervention
Xand maintenance of multiple aliases of the form "list, list-owner,
Xlist-request", etc. There is support provided for public and private 
Xhierarchical archives,
Xmoderated and non-moderated lists, peer lists, peer servers, private
Xlists, address aliasing, news connections and gateways, mail queueing,
Xdigests, list ownership, owner preferences, crash recovery, batch
Xprocessing, configurable headers, regular expressions, archive searching,
Xand live user connections via TCP/IP.
X.SH OBTAINING\ INFORMATION
X.TP
Xmailing lists, aliases, news groups, peer lists
XSee list(1).
X.TP
Xuser requests, list owners, peer servers, remote lists
XSee listserv(1).
X.TP
Xdistributing incoming mail
XSee catmail(1).
X.TP
Xarchiving files
XSee farch(1).
X.TP
Xarchiving of lists' messages
XSee below.
X.TP
Xprocessing the mail queue
XSee queue(1).
X.TP
Xoptions for the system's daemon
XSee serverd(1).
X.TP
Xstarting and stopping the system
XSee start(1).
X.TP
Xlive connections
XSee iul(1) and serverd(1).
X.SH SYSTEM\ SETUP
XThe UNIX ListServer system is installed under the HOMEDIR directory as defined
Xin src/Makefile; HOMEDIR points to the top level directory of the installation;
Xa \fIserver\fP user account has to be setup. The system should be installed and
Xset up using this account, and always be started from this account as well
X(see \fIstart\fP(1)).
XThen, the following alias
Xhas to be defined in /etc/aliases, /usr/lib/aliases  or /usr/ucblib/aliases
X(depending
Xon your system) for listserver (where users send their requests):
X.sp
X.in +2
Xlistserver: "|HOMEDIR/catmail -r -f"
X.in -2
X.sp
XKeep in mind, that the recommended alias before version 6.0 was "listserv",
Xso you may wish to use that for compatibility purposes.
X.PP
XVarious mailing lists are set up in a similar fashion; see \fIlist\fP(1).
X.PP
XIt is important to keep in mind that mailers should convert any text lines 
X(not header lines) of incoming messages starting with "From ", to ">From ".
XThis is automatically done by the -f flag to \fIcatmail\fP(1).
X.PP
XThe \fIcatmail\fP(1) utility is used to append incoming mail to the appropriate
Xfiles. When properly set up, it has the setuid bit turned on. Since
Xexecute permission is granted to everybody, it is possible for anyone
Xto append to these files; however, each time \fIcatmail\fP(1) is run, it
Xreports the user id and user name whom access was granted to.
X.PP
XIf the system is to go interactive (see \fIserverd\fP(1)), you have to add
Xthe following line in your /etc/services file:
X.sp
X.ce
Xulistserv   372/tcp
X.sp
XSee also src/Makefile.
X.PP
XHow the system operates is defined in the \fIconfig\fP file (see below).
XOnce the system is loaded, you will have to run the script \fIsetup\fP;
Xthis will ensure that all necessary files and directories are present, and
Xhave the right permissions. Before starting the
Xsystem, you may wish to edit the ".ignored" file in HOMEDIR (see \fIlist\fP(1)).
XAt this point, you may also wish to alter the help files in HOMEDIR/help;
Xeach file in that directory corresponds to one of the recognized requests.
XHelp files may be simple text files, or shell scripts starting with "#!"
Xin the first line, followed by the shell to execute.
X.PP
XFile protections are a major issue and the requirements may vary from system
Xto system. The problems usually arise from inadequate write permissions while
Xappending mail to the various mail files. The system uses the environment
Xvariable ULISTSERVER_UMASK in the same way as umask(1) is used when updating
Xfiles. If not set, the default is 066. A value of 026 should be acceptable in
Xmost cases. This variable should be set in both .cshrc and .profile.
X.PP
XFinally, experiment with the various mail methods described below, until
Xyou get the proper "From " line in the beginning of the outgoing message;
Xfor instance, when \fIlistserv\fP(1) is replying to a request, you should see
Xsomething like "From listserver@your-domain" as the first line of the header;
Xor when
Xthe list abc is distributing messages, this line should look like
X"From abc@your-domain". If you cannot get it to work, i.e. you get something
Xlike "From server@your-domain" then the server system may have
Xto run with superuser privileges. In this case, the server userid should
Xbe the same as root's. Also consult the PORT\ SPECIFIC section below,
Xfor suggested mailmethods. You will not have any of these problems
Xif you use the \fIsystem\fP mailmethod (see below).
X.PP
XIf this part of the setup is not done properly, peer lists will not
Xwork at all and users may not be able to reply to the list. As a final
Xnote, if \fItelnet\fP does not seem to be sending any mail, it is a
Xbug with the telnet implementation on your system: telnet
Xcannot have its input redirected or piped, and should be reported to your
Xvendor.
X.PP
XThe last step for a complete set up is to define your system in the
X\fIconfig\fP file.
X.SH CONFIG
XThe server system is defined in the \fIconfig\fP file; an example
Xfile is provided with the system. This file is used by \fIstart\fP(1),
X\fIserverd\fP(1), \fIlist\fP(1) and \fIlistserv\fP(1), and is not a shell
Xscript.
X.PP
XThe following keywords (directives) are recognized and is advisable that each
Xone of them starts at column 1; directives may span multiple lines if each
Xline is terminated by &\\n:
X.TP
X\fIorganization\fP name
XThis defines your organization when posting to news and upon connection
Xestablishment of a live session. \fIname\fP may
Xinclude blanks.
X.TP
X\fIserver\ listserver\fP@your-domain\ command-line-options
XThis defines the list server; the first argument is the full email address
Xof the server (e.g. listserver@foo.edu) followed by any command line
Xoptions to be used when \fIserverd\fP spawns \fIlistserv\fP
X(see \fIserverd\fP(1), \fIlistserv\fP(1)).
X.TP
X\fIlist\fP\ list_alias\ email-addr\ owner-addr\ password\ cmd-line-opt
XThis defines a mailing list; the first argument is the list's alias
Xin /etc/aliases, /usr/lib/aliases  or /usr/ucblib/aliases (see \fIlist\fP(1)),
Xfollowed by its full email
Xaddress, followed by its owner's full email address, followed by the
Xlist's password for maintenance, followed by any command line options
Xto be used when \fIserverd\fP(1) spawns \fIlist\fP(1).
X.TP
X\fIheader\fP\ list\ {\ [Header-Line:]\ ...\ }
XIt specifies precious header lines from the original sender's message
Xthat are to be preserved during distribution. The \fIlist\fP has to be
Xdefined before, and precious header lines are specified within the
Xenclosing \fI{\fP and \fI}\fP, possibly spanning multiple lines.
X.TP
X\fIdefault\fP\ list\ {\ [option = value]\ ...\ }
XDetermine various default values for \fIlist\fP for new and current subscribers;
Xthe list has to be defined before. Pairs of options and values may be repeated
Xany number of times, and may span multiple lines; if certain options are
Xmissing, then system defaults are used. Valid \fIoption\fPs are:
X.sp
X.in +2
X\fIaddress\fP: turn user-settable subscription addresses on or off. Valid values
Xare:
X.sp
X.in +2
X\fIfixed\fP: users are not allowed to change the address they are subscribed
Xwith via the \fIset <list> address\fP request (see \fIlistserv\fP(1)).
XThis is the system default as well.
X.sp
X\fIvariable\fP: opposite of the above.
X.in -2
X.sp
X\fImail\fP: select the default mail mode for new subscribers. Valid values are:
X.sp
X.in +2
X\fIack\fP: see \fIset\fP in \fIlistserv\fP(1).
X.sp
X\fInoack\fP: see \fIset\fP in \fIlistserv\fP(1); this is the system default.
X.sp
X\fIpostpone\fP: see \fIset\fP in \fIlistserv\fP(1) (a rather useless setting indeed).
X.sp
X\fIdigest\fP: see \fIset\fP in \fIlistserv\fP(1).
X.in -2
X.sp
X\fIpassword\fP: define a single password for new users; if missing, the system
Xassigns a random password. Any string that contains no white spaces may be
Xused.
X.sp
X\fIconceal\fP: determine the default visibility of new users; see \fIset\fP
Xin \fIlistserv\fP(1). Valid options are:
X.sp
X.in +2
X\fIyes\fP: hidden from \fIrecipients\fP and \fIstatistics\fP requests.
X.sp
X\fIno\fP: this is the system default.
X.in -2
X.in -2
X.TP
X\fIceiling\fP\ list\ max_messages
XRestrict the number of messages processed daily for \fIlist\fP to
X\fImax_messages\fP; this includes posted and rejected messages.
X.TP
X\fIremote\fP\ alias\ email-addr\ listserver-addr\ [host\ [port]]\ #Comment
XThis makes a remote list known to the server. A remote list is another
Xmailing list served by another server, and your server is now capable
Xof forwarding to the proper remote list server any requests sent about
Xthis remote list to your server. \fIalias\fP is the name by which the remote
Xlist is known by, \fIemail-addr\fP is the full email address of the
Xremote list, \fIlistserver-addr\fP is the full email address of the
Xremote list server, and \fI#Comment\fP
Xwill be used upon a \fIlists\fP request made to this server (the pound
Xsign is mandatory). \fIhost\fP is either the DNS name or IP address
Xof the remote server, used to connect to that server when a live request
Xrefers to that list; the \fIport\fP should be specified if not the default
X(372) (see \fIserverd\fP(1)).
X.sp
XRemote lists may have the same \fIalias\fP and they should not be confused
Xwith peer lists. However, each remote list should be defined only once.
XSee the discussion about PEER\ SERVERS (\fIlistserv\fP(1)) for further information.
X.TP
X\fIfax\fP fax-program options
XDefine the fax program to use for \fIfax\fP requests (if any) and any
Xcommand line options the program may use. The program should also be capable
Xof accepting the file to fax from its standard input; otherwise a script
Xwill have to be used instead which will in turn use the fax program
Xappropriately. If this directive is left or commented out, then \fIfax\fP
Xrequests are automatically turned of.
X.sp
XThe actual faxing program is called as follows:
X.sp
X.ce
X\fIfax-program\fP \fIoptions\fP fax-number < file
X.sp
XThis request may not be issued from a live session, since fax programs work
Xon email messages and extract the sender's address for reference when sending
Xthe cover page of the fax.
X.TP
X\fIunix_cmd\fP\ list_alias\ password\ name\ \'unix-command\ [args]\'\ #Comment
XAllow a \fIlist_alias\fP's subscribers only to execute the above
X\fIunix-command\fP aliased to \fIname\fP. The \fIpassword\fP is made known
Xto users who are to be given this privilege. The oprional \fIargs\fP may be
Xliteral arguments to the command as well as the special sequences \fI$n\fP,
Xwhere \fIn\fP is a digit from 1 to 9, or *; their meaning is that of the Bourne
Xshell, and they will be substituted by the user's actual arguments. For
Xexample:
X.sp
X.ce
Xunix_cmd ermis pass1 swap \'/bin/echo $2 $1\' #Syntax: swap <arg1> <arg2>
X.sp
Xwill swap the user's first and second arguments. The user would submit a
Xrequest that could look like this:
X.sp
X.ce
Xrun ermis pass1 swap arg1 arg2
X.sp
XNo security checks are made except to ensure that the arguments contain no `, |,
X:, < and >, so the \fImanager\fP is encouraged to review
Xand verify the proper functioning of \fIunix-command\fP. The \fIComment\fP
Xis used when users issue \'run <list>\' query requests.
X.TP
X\fIserverd\fP\ command-line-options
XThis defines the command line options that \fIstart\fP(1) is to use
Xwhen spawning \fIserverd\fP(1).
X.TP
X\fIrestriction\fP\ nusers
XIf a restriction is placed on a ListServer request (via a -r command line
Xoption to \fIlistserv\fP(1)), this directive defines the threshold number of users,
Xabove which the restriction will take effect.
X.TP
X\fIdisable\fP\ list_alias request
XThe specified server \fIrequest\fP is disabled for the specified list, and
Xall such requests for this list are rejected, except when issued by privileged
Xusers (list owners). The \fIlist_alias\fP has to
Xbe defined (via a \fIlist\fP directive) before any requests can
Xbe disabled.
X.TP
X\fImanager\fP\ full-email-address
XThis defines the recipient of all system error messages, and the system's
Xadministrator and caretaker; \fIfull-email-address\fP
Xcan be any valid user name that can be reached via email by your system.
XThe \fImanager\fP is usually the person responsible for the server
Xaccount.
X.TP
X\fIcomment server\fP\ #Actual comment
X.TP
X\fIcomment\fP\ list_alias\ #Actual comment
XA 'X-Comment:' line is included in every outgoing list/server message, and the
Xtext following the pound sign will be copied every time; note that the pound
Xsign is mandatory for the definition but will not be part of the actual string.
XA list's comment is also used for the \fIlists\fP request to
Xspecify the purpose of the particular mailing list. No 'X-Comment:' line
Xis included if this directive is not defined for the particular list and/or
Xserver.
X.TP
X\fIdigest\fP\ list_alias\ lines\ hours
XIf any subscriber to the list requests digests, a digest will be sent when
Xits length exceeds \fIlines\fP, or when \fIhours\fP have passed
Xwithout a digest being sent. Digests for a list are collected only if at
Xleast one of its subscribers has set his mail mode to \fIdigest\fP.
X.TP
X\fIarchive\fP\ list_alias\ dir\ filename\ [archive]\ [password]\ [digest]
XRequest that all public messages are automatically archived; \fIlist_alias\fP
Xis the list's name, \fIdir\fP is the directory that the archived
Xfiles will be located (a full path has to be specified, and the directory
Xhas to be writable by the server uid), \fIarchive\fP is the archive's name
Xand is a relative path under the default archive (listserver) or "-" for the
Xdefault, \fIpassword\fP is an optional password ("-" if none) to be used
Xif the archive is meant to be private, and the optional
Xkeyword \fIdigest\fP will force only digests to be archived; \fIfilename\fP
Xspecifies the format of the archived files, and may contain any lower case
Xcharacters and numbers, as well as the following special sequences:
X.in +2
X.sp
X%m: Numeric month (01 - 12)
X.sp
X%d: Numeric day of the month (01 - 31)
X.sp
X%y: Year (00 - 99)
X.sp
X%j: Julian date (001 - 366)
X.sp
X%h: Month name (Jan - Dec)
X.sp
X%a: Contents of the Archive-Name: header line, if present; cannot be used if
Xarchiving digests. The Archive-Name: header line has to be manually inserted
Xby the sender of the message; it may show up in the body of the message as
Xwell.
X.sp
X%#: Digest number; can only be used if archiving digests.
X.sp
X%1: First word of the first non-blank line of the message.
X.sp
X%v: Volume number, if the message contains a 'Volume # Number #' line before
Xthe actual message.
X.sp
X%n: Issue number, if the message contains a 'Volume # Number #' line before the
Xactual message.
X.sp
X%%: The character %
X.in -2
X.sp
XFor example, to archive messages daily:
X.sp
X.ce
Xarchive ermis HOMEDIR/archives/lists/ermis %y%m%d listserver/lists/ermis
X.sp
Xor to archive only files sent to the list:
X.sp
X.ce
Xarchive venus /ftp/lists/venus vol-%v.num-%n listserver/lists/venus johny-be-good
X.sp
Xand in this case the sender that wants a file to be archived would precede
Xit with a line similar to:
X.sp
X.nf
XVolume 10 Number 5
X.fi
X.sp
Xfollowed by the actual file.
X.sp
XWhen a new archive file is created, the Subject: line is used as the short
Xdescriptive message for the archive.
X.TP
X\fIfrequency\fP\ seconds
XHow often should \fIserverd\fP(1) check for new mail. When \fIseconds\fP is
Xset to zero, \fIserverd\fP does not sleep.
X.TP
X\fIbatch\fP\ start\ stop
XSpecify the hours (in military time) when requests are to be batched.
XThe defaults are 8 and 20. \fIstart\fP and \fIstop\fP should be within
Xthe same day (e.g. 8 am and 1 am the following day are not valid).
X.TP
X\fIlimit\fP\ keyword\ arguments
XUsed to set certain limits in the system; \fIkeyword\fP can be:
X.in +2
X.sp
X\fImessage\fP: limit the size of distributed messages to a certain
Xnumber of bytes, which is the \fIargument\fP following this keyword.
XA notification is sent back to the sender when this limit is exceeded,
Xincluding the first few lines of his/her original message for reference.
X.in -2
X.sp
X.in +2
X\fIfiles\fP: limit the size of archive files that can be sent out to a certain
Xnumber of bytes, which is the \fIargument\fP following this keyword.
XWhen this limit is reached, the file is automatically split into subparts.
X.TP
X\fIprecedence\fP\ string
XThe header of each outgoing message contains a Precedence: line; \fIstring\fP
Xcan be:
X.in +2
X.sp
Xbulk, junk, first-class, none: primarily used for vacation programs.
X.in -2
X.TP
X\fIoption\fP\ keyword
XThis defines a series of system-dependent options; \fIkeyword\fP can be:
X.in +2
X.sp
X\fIsysv_ps\fP: the system will assume a System V version of ps(1).
X.sp
X\fIbsd_ps\fP: the system will assume a BSD version of ps(1).
X.sp
X\fIbsd_mail\fP: define it if BSD (UCB) mail is available on your system; if this
Xis the case, make sure that /usr/ucb/mail is the path (or a link) to it; also
Xsee src/Makefile.
XThis is used to notify the \fImanager\fP of any error conditions.
X.sp
X\fIbad_telnet\fP: if the mail method used (see below) is \fItelnet\fP and 
Xthe system seems to send out only one message and then go to sleep, use
Xthis option.
X.sp
X\fIpost_mail\fP: this will force the system to post messages to news
Xgroups (using \fIinews\fP), using the groups' names (e.g. misc.test). Make
Xsure that \fIinews\fP resides in /usr/lib/news, or 
X/usr/lib/news/inews is a link to it (also see src/Makefile).
X.sp
X\fIgate_mail\fP: this will force the system to gate messages to news, using
Xthe gateways' email addresses.
X.sp
X\fIignore_invalid_requests\fP: ignore all unrecognized user requests and
Xprocess valid ones only; by default, the system aborts processing requests
Xupon the first invalid one, in which case a reply is sent to the user and
Xall subsequent requests are flushed.
X.sp
X\fIrelaxed_syntax\fP: by default, the system complains when a request is
Xgiven more arguments than expected. This turns such checking off.
X.in -2
X.TP
X\fImailmethod\fP\ method\ [arguments]
XEvery outgoing
Xmessage should begin with a line of the form:
X.in +2
X.sp
XFrom listserver@your-domain
X.sp
Xor
X.sp
XFrom list_alias@your-domain
X.sp
X.in -2
Xwhich depends on the mail \fImethod\fP used:
X.in +2
X.sp
X\fIsystem\fP: the recommended method; it provides a unified approach to
Xsending mail and it has been ported and tested on lots of systems (see
Xthe section PORT\ SPECIFIC below).
XIn addition, since a full set of the SMTP protocol is implemented,
X\fImanager\fP and list owners will be notified of invalid addresses and
Xvarious system problems.
XMoreover, mail will be queued when it cannot be delivered, in the
Xdirectory HOMEDIR/mqueue; see \fIqueue(1)\fP for more information
Xon how to process the mail queue.
X.sp
XThis method requires host TCP/IP and internet support; consult the
Xsrc/README file for more information.
X.sp
X\fItelnet\fP: to be used only when \fIsystem\fP is inappropriate and telnet(1)
Xis available on your host.
X.sp
X\fIenv_var\fP: usually followed by \fILOGNAME /bin/rmail\fP,
Xor \fILOGNAME /usr/lib/sendmail -ba\fP (-ba is mandatory) -- to be 
Xused only when the previous methods are inappropriate.
X.in -2
X.SH MAIL\ LOOPS
XThe system uses the following protocol for avoiding mail loops between
Xa list and news connections: In the header of the outgoing message an
X"Originator: " field is added. For each list plus listserver, a log of
Xthe most recent (500) Message-Id's is kept.
XWhenever a message is received, the
XOriginator, Reply-To and Message-Id fields are extracted and looked up in
Xthe ".ignored" and ".message.ids"
Xfiles in the list's subdirectory. Gateways that feed back to the list
Xshould preserve at least the Reply-To and Message-Id fields. The Originator
Xand Message-Id fields are preserved by this system. A new Reply-To is
Xtacked on when redistributing mail locally from a peer or a news feed.
X.PP
XTo avoid mail loops when forwarding ListServer requests to peers, the system
Xlooks up the Message-Id field in the ".message.ids" file in 
XHOMEDIR.
XIf this field is not preserved by the peer ListServer, it is suggested that you
Xturn off request forwarding when connecting with peers served by such
Xsystems, until a unified approach is taken. The Message-Id field is preserved
Xby this ListServer. This system is also using a special Subject field,
Xtotally proprietary and non-standard, in order to avoid unnecessary
Xforwarding of requests, thus cutting down on email traffic.
X.SH CRASH\ RECOVERY
XIt is possible that a message delivery was interrupted by a user
X(\fIstart -k\fP), or by the system (crash/reboot). A built-in mechanism
Xguarrantees to pick up delivery again from where it left off, if the
Xsystem is restarted using \fIstart\fP(1). Both \fIstart\fP(1) and \fIlist\fP(1)
Xreport to the effect that mail delivery was interrupted and will
Xresume. On the other hand, processing of interrupted requests will not
Xresume and all unprocessed requests will be lost.
X.SH COMMUNICATING
XUsers send requests to \fIlistserver@your-domain\fP and public messages to the
Xvarious \fIlist_alias@your-domain\fP.
X.SH ARCHIVES
XThe server has an archiving capability of files. Archives may be public
Xso that anyone can get files from them, or private so that only privileged
Xusers may obtain files from them. There is a master archive
Xin HOMEDIR/archives/listserver where all subarchives are defined. The archives
Xkeep lists of files that are available to users via a \fIget\fP request, and
Xindex of subarchives that are available to users via an \fIindex\fP
Xrequest.
X.PP
XAs outlined in \fIlist\fP(1), each list's public messages are
Xautomatically saved under the list's subdirectory in the file \fIarchive\fP.
XThis file may periodically be placed in the system's archives by hand (if not
Xarchiving automatically via the \fIarchive\fP directive), or may point to
X/dev/null. The archived files may not necessarily reside in
Xthe archive directories, as long
Xas the archives know where they are located. See the man page for
X\fIfarch(1)\fP for more information.
X.PP
XAlternatively, messages may be automatically archived via the \fIarchive\fP
Xdirective in the \fIconfig\fP file.
X.SH HELP
XThe system comes configured with default help topics the recognized
Xrequests. More topics may be added by editing the file
XHOMEDIR/help/TOPICS, adding the topic(s) and the file(s) where the
Xactual text exists.
X.PP
XHelp files may be simple text files or shell scripts, in which case they
Xhave to start with "#!" in the first line and be followed by the path
Xto the shell to be used; for example: #!/bin/sh
X.SH SYSTEM\ MAINTENANCE
XAs mentioned below, all programs report to certain files. The system's
Xmanager should periodically check the files HOMEDIR/.report.server
Xand the various HOMEDIR/lists/*/.report.list for any problems.
XAll messages sent are saved under HOMEDIR/mbox and 
XHOMEDIR/lists/*/mbox and they should be cleaned up periodically.
XFor debugging purposes, various warnings are written to 
XHOMEDIR/.warning; this file, as well as HOMEDIR/.report.catmail
Xare shrunk every time \fIstart\fP(1) is run.
X.PP
XFor whenever the necessity arises to administer the system remotely, the
Xfollowing scheme may be used to start or kill the system, process the mail
Xqueue, force an application to execute immediately, etc: a mail message
Xis sent to an alias whose sole role is to fire up an application; the
Xapplication has to have been setuid before.
X.PP
XFor instance, to start the
Xsystem remotely, you may set up an alias such as:
X.sp
X.nf
X.in +2
Xstart-server: "|HOMEDIR/start -cr > /dev/null"
X.in -2
X.fi
X.sp
XTo kill the system, you may define another alias:
X.sp
X.nf
X.in +2
Xkill-server: "|HOMEDIR/start -crk > /dev/null"
X.in -2
X.fi
X.sp
XTo force requests to get processed now:
X.sp
X.nf
X.in +2
Xrun-listserver: "|HOMEDIR/listserv -1 > /dev/null"
X.in -2
X.fi
X.sp
XTo start the queue daemon:
X.sp
X.nf
X.in +2
Xproc-queue: "|HOMEDIR/queued 60 & > /dev/null"
X.in -2
X.fi
X.sp
XThese aliases should be hard to guess for obvious reasons. The actual
Xmessage sent to such an alias is discarded.
X.SH EXIT\ CODES
XAll applications except tlock use the following exit codes for communication:
X.sp
X.TP
X0
X- OK, normal exit
X.TP
X1
X- Could not open or lock file
X.TP
X2
X- SIGINT signal (normal termination)
X.TP
X3
X- Command line option error
X.TP
X4
X- Syntax error in file
X.TP
X5
X- Could not spawn (restart failed)
X.TP
X6
X- Shutdown request
X.TP
X7
X- Restart request
X.TP
X8
X- Received system signal (anything but SIGINT will bring the system down)
X.TP
X9
X- Too many multiple recipients
X.TP
X10
X- Could not deliver mail (unexpected reply during the SMTP transaction)
X.TP
X11
X- malloc() failed
X.TP
X12
X- Cannot fork()
X.TP
X13
X- Socket connection problem
X.TP
X14
X- Semaphore error
X.TP
X15
X- Cannot setuid()/setgid()
X.TP
X16
X- Internal error or system call failure
X.fi
X.PP
Xtlock exits with:
X.TP
X-1
X- File locking not functional
X.TP
X0
X- No files locked
X.TP
X1
X- Cannot open config
X.TP
X2 & up
X- Number of files locked + 1
X.SH REPORTS
XThe system provides two ways of reporting progress: via syslog(3) when available
X(by compiling with -DSYSLOG=facility) with priority LOG_INFO and facility as
Xspecified, or through its own report files (when not using syslog(3)); these
Xare:
X.TP
XHOMEDIR/.report.catmail
XMessages from \fIcatmail\fP(1) every time new messages are appended to the
Xvarious mail files.
X.TP
XHOMEDIR/.report.daemon
XMessages from \fIserverd\fP(1) every time new mail has arrived.
X.TP
XHOMEDIR/.report.list
XError messages from \fIlist\fP(1) when attempting to process public messages.
X.TP
XHOMEDIR/.report.pqueue
XMessages from \fIpqueue\fP (see \fIqueue\fP(1)).
X.TP
XHOMEDIR/.report.server
XMessages from \fIlistserv\fP(1) every time new requests are processed.
X.TP
XHOMEDIR/.report.start
XGenerated by \fIstart\fP(1) every time the system is started.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.report.list
XMessages from \fIlist\fP(1) when processing public messages.
X.TP
XHOMEDIR/.*.acc
XAccumulated reports since the system was first installed.
X.TP
XHOMEDIR/lists/*/.*.acc
XAccumulated reports for each mailing list.
X.PP
XEvery report includes a time stamp; every list and server report also
Xincludes the actual sender of the message, and every server report
Xincludes all requests made by the sender.
X.PP
XWhenever a program dies abnormally (and BSD mail is present) a message
Xis sent to \fImanager\fP (as defined in \fIconfig\fP). 
XThe message is usually sent by \fIserverd\fP(1) which
Xthen exits. The daemon dies with a different message according to the exit
Xstatus of the child; the various error conditions that may occur are:
X.TP
XCould not open file
XHOMEDIR is not a valid path; file was accidentally deleted;
Xinsufficient access privileges. Run \fIsetup\fP and \fIstart\fP.
X.TP
XCould not lock file
XHOMEDIR is not a valid path; file was accidentally deleted;
Xinsufficient access privileges; another program is using the lock
Xfile. Run \fItlock\fP, and if necessary, \fIulock\fP; then \fIstart\fP.
X\fItlock\fP checks for locked files, and \fIulock\fP removes all lock
Xfiles.
X.TP
XCommand line option error
XCheck the \fIconfig\fP file and restart.
X.TP
XSyntax error in file
XCheck \fIconfig\fP, all reports, .subscribers, .peers and .news files.
X.TP
XCould not spawn
XNo more processes.
X.TP
XReceived system signal
XSIGQUIT, SIGTERM, SIGBUS, SIGSEGV, SIGILL.
X.SH FILES
X.TP
XHOMEDIR/.aliases
XAliases of email addresses of users having trouble getting replies to requests.
X.TP
XHOMEDIR/.awk
Xawk program for formatting the \fIstatistics\fP request.
X.TP
XHOMEDIR/.grep
Xscript used for counting the number of messages sent by a subscriber.
X.TP
XHOMEDIR/.ignored
XList of unwanted senders.
X.TP
XHOMEDIR/.lock.list
XLock file for \fIlist\fP(1).
X.TP
XHOMEDIR/.lock.pqueue
XLock file for \fIpqueue\fP (see \fIqueue\fP(1)).
X.TP
XHOMEDIR/.lock.server
XLock file for \fIlistserv\fP(1).
X.TP
XHOMEDIR/.lock.serverd
XLock file for \fIserverd\fP(1).
X.TP
XHOMEDIR/.message.ids
XA database of recent message id's used to detect mail loops.
X.TP
XHOMEDIR/.queue.id
XNext id to be assigned to next undeliverable message placed in the mqueue/
Xdirectory.
X.TP
XHOMEDIR/.reply.listser
XThe reply code given to \fIserverd\fP(1) during a live session.
X.TP
XHOMEDIR/.rep.server.acc
XArchived \fIlistserv\fP(1) reports.
X.TP
XHOMEDIR/.rep.serverd.acc
XArchived \fIserverd\fP(1) reports.
X.TP
XHOMEDIR/.rep.start.acc
XArchived \fIstart\fP(1) reports.
X.TP
XHOMEDIR/.report.catmail
XCurrent \fIcatmail\fP(1) report.
X.TP
XHOMEDIR/.report.daemon
XCurrent \fIserverd\fP(1) report.
X.TP
XHOMEDIR/.report.list
XError messages from \fIlist\fP(1).
X.TP
XHOMEDIR/.report.server
XCurrent \fIlistserv\fP(1) report.
X.TP
XHOMEDIR/.report.start
XLast \fIstart\fP(1) action (system started or shut down).
X.TP
XHOMEDIR/.stats
XScript used for the \fIstatistics\fP request.
X.TP
XHOMEDIR/.tag.id
XNext id to be assigned to next moderated message.
X.TP
XHOMEDIR/.warning
XLog of system calls' exit statuses.
X.TP
XHOMEDIR/batch
XBatched user requests.
X.TP
XHOMEDIR/catmail
XIncoming message redirection application.
X.TP
XHOMEDIR/config
XThe configuration file.
X.TP
XHOMEDIR/farch
XFile archiving utility.
X.TP
XHOMEDIR/iul
XInteractive UNIX ListServer client; to be used to connect to the
Xinteractive part of the system.
X.TP
XHOMEDIR/list
XThe mailing list program.
X.TP
XHOMEDIR/listserv
XThe system's request server.
X.TP
XHOMEDIR/lost+found
XRequests that could not be appended to HOMEDIR/requests.
X.TP
XHOMEDIR/mbox
XAn archive of all requests sent to date to ListServer.
X.TP
XHOMEDIR/news
XScript used to add a news group to a list.
X.TP
XHOMEDIR/owners
XList of list owners' addresses used for authentication of list maintenance
Xrequests.
X.TP
XHOMEDIR/peer
XScript used to add a peer list.
X.TP
XHOMEDIR/pqueue
XThe mail queue processing program (see \fIqueue(1)\fP).
X.TP
XHOMEDIR/priv.hosts
XList of hosts that may connect to the interactive part of the system.
X.TP
XHOMEDIR/queued
XThe mail queue processing daemon (see \fIqueue(1)\fP).
X.TP
XHOMEDIR/received
XFile containing the messages received from sendmail during the
Xlast message distribution -- the \fIsystem\fP mailmethod has to be
Xin use.
X.TP
XHOMEDIR/requests
XNewly arrived user requests.
X.TP
XHOMEDIR/requests.live
XRepository for requests coming in from live connections.
X.TP
XHOMEDIR/sent
XFile containing the messages sent to sendmail during the
Xlast message distribution -- the \fIsystem\fP mailmethod has to be
Xin use.
X.TP
XHOMEDIR/serverd
XThe system's daemon.
X.TP
XHOMEDIR/setup
XSystem setup script.
X.TP
XHOMEDIR/start
XThe system's housekeeping program.
X.TP
XHOMEDIR/tlock
XChecks for any locked files.
X.TP
XHOMEDIR/ulock
XScript to remove all lock files.
X.TP
XHOMEDIR/unwanted.hosts
XList of hosts that may not connect to the interactive part of the system.
X.TP
XHOMEDIR/welcome.live
XWelcoming message upon live connection establishment.
X.TP
XHOMEDIR/archives/listserver/DIR
XDirectory of all files available from the master archive (listserver).
X.TP
XHOMEDIR/archives/listserver/INDEX
XThe master archive index.
X.TP
XHOMEDIR/doc/catmail.1
XMan page source for \fIcatmail\fP(1).
X.TP
XHOMEDIR/doc/catmail.nr
XFormatted man page for \fIcatmail\fP(1).
X.TP
XHOMEDIR/doc/farch.1
XMan page source for the \fIfarch\fP(1) utility.
X.TP
XHOMEDIR/doc/farch.nr
XFormatted man page for the \fIfarch\fP(1) utility.
X.TP
XHOMEDIR/doc/iul.1
XMan page source for the \fIiul\fP(1) utility.
X.TP
XHOMEDIR/doc/iul.nr
XFormatted man page for \fIiul\fP(1).
X.TP
XHOMEDIR/doc/list.1
XMan page source for \fIlist\fP(1).
X.TP
XHOMEDIR/doc/list.nr
XFormatted man page for \fIlist\fP(1).
X.TP
XHOMEDIR/doc/listserv.1
XMan page source for \fIlistserv\fP(1).
X.TP
XHOMEDIR/doc/listserv.nr
XFormatted man page for \fIlistserv\fP(1).
X.TP
XHOMEDIR/doc/queue.1
XMan page source for \fIpqueue\fP and \fIqueued\fP(1).
X.TP
XHOMEDIR/doc/queue.nr
XFormatted man page for \fIpqueue\fP and \fIqueued\fP(1).
X.TP
XHOMEDIR/doc/server.1
XSource of the system's main man page.
X.TP
XHOMEDIR/doc/server.nr
XFormatted main man page.
X.TP
XHOMEDIR/doc/serverd.1
XMan page source for \fIserverd\fP(1).
X.TP
XHOMEDIR/doc/serverd.nr
XFormatted man page for \fIserverd\fP(1).
X.TP
XHOMEDIR/doc/start.1
XMan page source for \fIstart\fP(1).
X.TP
XHOMEDIR/doc/start.nr
XFormatted man page for \fIstart\fP(1).
X.TP
XHOMEDIR/help/*
XGeneral and topic-specific help files available upon a \fIhelp\fP
Xrequest. The TOPICS is a special file where topics are defined.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.aliases
XAliases of email addresses of subscribers.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.digest.toc
XAll the subject lines from the digest. It will be
Xwritten at the head of the digest when it is sent.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.digest.msg
XAll the messages of the digest, separated by dashed lines.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.digest.time
XThe time that the last digest was sent, represented as the value
Xreturned by time(2). It is used to calculate when it is time to send
Xout the next digest.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.headers
XAn archive of who sent each message; used upon a \fIstatistics\fP
Xrequest.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.ignored
XA list of unwanted senders.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.info
XText sent upon an \fIinformation\fP request.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.limits
XUsed by \fIserverd\fP(1) when placing various limits for the list.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.message.ids
XA database of recent message id's used to detect mail loops.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.news
XA list of all news groups connected to this list.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.peers
XA list of all peer lists for this mailing list,
Xalong with the email addresses of their servers.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.rep.list.acc
XArchived reports from \fIlist\fP(1).
X.TP
XHOMEDIR/lists/LIST_ALIAS/.report.list
XCurrent report from \fIlist\fP(1).
X.TP
XHOMEDIR/lists/LIST_ALIAS/.restricted
XList of subscribers with alternate recipient files.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.subscribers
XA list of all subscribers along with preferences and settings.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.un.digest
XWhen a digest is constructed, it will be stored in this file until it
Xhas been sent to all digest subscribers.
X.TP
XHOMEDIR/lists/LIST_ALIAS/.welcome
XText sent on a \fIsubscribe\fP request.
X.TP
XHOMEDIR/lists/LIST_ALIAS/archive
XArchive of all distributed messages since the file was last created.
X.TP
XHOMEDIR/lists/LIST_ALIAS/lost+found
XMail that could not be appended to the 'mail' or 'moderated' files
X(see next entries and \fIcatmail\fP(1)).
X.TP
XHOMEDIR/lists/LIST_ALIAS/mail
XNewly arrived public messages to be distributed.
X.TP
XHOMEDIR/lists/LIST_ALIAS/mbox
XAn archive of all messages sent to date.
X.TP
XHOMEDIR/lists/LIST_ALIAS/moderated
XNewly arrived public messages that need to be edited before
Xdistributed.
X.TP
XHOMEDIR/lists/LIST_ALIAS/removed.users
XEntries from the .subscribers file for users who have been automatically
Xremoved from the list.
X.TP
XHOMEDIR/lists/LIST_ALIAS/removed.alias
XEntries from the .aliases file for users who have been automatically
Xremoved from the list.
X.TP
XHOMEDIR/mqueue/*
XQueued messages for later delivery.
X.SH UPGRADING
XWhen upgrading, it is not necessary to remove your current system.
XOld key directories and files will be moved elsewhere. Always run the
X\fIsetup\fP script. In addition, if you are upgrading from:
X.TP
X5.5
XThe system may now go interactive with the -i flag to \fIserverd\fP(1).
X.sp
X\fIremote\fP lists may now specify host names or Internet addresses and ports if
Xthey are served by interactive UNIX ListServers.
X.sp
XThe ULISTSERVER_UMASK environment variable is now used.
X.sp
XThe IUL (\fIiul(1)\fP) client is available to connect to interactive UNIX
XListServers.
X.sp
XThe \fIhelp\ live\fP request explains how to connect to interactive UNIX
XListServers.
X.sp
XThe \fIprecedence\fP directive has been added to the \fIconfig\fP file.
X.sp
XPrecious header lines are preserved for each list via the \fIheader\fP
Xdirective.
X.sp
XRegular expressions are implemented for the ".aliases" and ".ignored" files.
X.sp
XLists may now automaticlly archive distributed messages via the
X\fIarchive\fP directive.
X.sp
XThe manager may allow subscribers of a list to execute UNIX commands via the
X\fIunix_cmd\fP directive. Users submit a \fIrun\fP request in this case
X(CCRUN is added as a preference).
X.sp
X\fIfarch\fP now creates new archives and takes care of updating and creating
XDIR and INDEX files as well as directories, and may archive true binary files
X(does not uuencode them) if requested (-B flag); the -p option was introduced
Xto accept a password for a new private archive, the -D option to specify
Xa short description of the files, and the -r option to remove files from
Xarchives.
X.sp
XCommands in the \fIconfig\fP file may span multiple lines if each line is
Xterminated by &\\n.
X.sp
XThe system intercepts requests sent to lists.
X.sp
XThe \fIconceal\fP user attribute has been introduced to the subscribers files.
X.sp
XThe \fIdefault\fP directive has been introduced to the \fIconfig\fP file to
Xset default values for new subscribers.
X.sp
XUsers may change the address they are subscribed with, if the owner allows this.
X.sp
XThe util/ directory has been introduced for general utilities.
X.sp
XHelp files may now be shell scripts; same for all lists' ".welcome" and
X".info" files.
X.sp
Xsyslog(3) is now supported for progress reporting by compiling with
X-DSYSLOG=facility.
X.sp
XArchive files may be split on the way out if they exceed the \fIlimit\fP
Xspecified in the \fIconfig\fP file.
X.sp
XLists may restrict the number of messages processed per day with the
X\fIceiling\fP directive.
X.sp
XThe new request \fIfax\fP with its synonymous directive have been introduced.
X.sp
XConcealed lists are supported via the -c option to \fIlistserv\fP(1).
X.sp
XError messages from mailer daemons are scanned and automatic action is taken
Xif compiling with -DERROR_MAIL_ANALYSIS=level (1=conservative, 9=full).
X.sp
XArchives may now be searched with the \fIsearch\fP request.
X.sp
XRun the upgrade_to_6.0 script.
X.TP
X5.41
XModerated lists are now owner controlled, rather than manager controlled.
X.sp
XManager preferences have now been introduced.
X.sp
XSee the UPGRADING section in farch(1).
X.sp
XFollow the upgrade instructions for 5.5
X.TP
X5.4
X\fIcatmail\fP(1) has been implemented, therefore the various aliases
Xin the aliases file have to be redefined (as explained above) to
Xtake advantage.
X.sp
XOwner preferences have been introduced in the \fIowners\fP file.
X.sp
XFollow the upgrade instructions for 5.41
X.TP
X5.31
XThe \fIlist\fP directive in the \fIconfig\fP file now takes two extra
Xarguments: the list owner's address, and the list's access password.
X.sp
X\fIqueued\fP now takes only one argument, a frequency count.
X.sp
XThe system supports 10 lists by default (see LIMITATIONS below);
Xif you are currently using the maximum supported number of lists, consult
Xthe HOMEDIR/src/defs.h file.
X.sp
XThe layout and structure of INDEX files has been extended; see
X\fIfarch(1)\fP for more information.
X.sp
XFollow the upgrade instructions for 5.4
X.TP
X5.3
XThe \fIlist\fP directive in the \fIconfig\fP file now takes two extra
Xarguments: the list owner's address, and the list's access password.
X.sp
XThe system supports 10 lists by default (see LIMITATIONS below);
Xif you are currently using the maximum supported number of lists, consult
Xthe HOMEDIR/src/defs.h file.
X.sp
XThe layout and structure of INDEX files has been extended; see
X\fIfarch(1)\fP for more information.
X.sp
XFollow the upgrade instructions for 5.4
X.TP
X5.2\*
XUse the \fIsystem\fP mailmethod.
X.sp
XFor every list you need to edit HOMEDIR/lists/LIST_ALIAS/.ignored
Xand add one more entry: the full email address of the server account.
X.sp
XFollow the upgrade instructions for 5.3
X.TP
X5.0, 5.1\*
XFor every list you need to edit
XHOMEDIR/lists/LIST_ALIAS/.ignored and add
Xtwo entries: the actual list alias, and the full email address
Xof this list.
X.sp
X\fIneverack\fP is not a valid mail mode anymore. Change every
Xoccurrence in ".subscribers" to NOACK, for every list.
X.sp
XAny peer lists and/or news groups appearing in ".subscribers"
Xshould be removed; use the \fIpeer\fP and \fInews\fP scripts
Xto put them back.
X.sp
X\fIserverd\fP, \fIlist\fP and \fIlistserv\fP no longer report to the
Xstdout by default -- use the -e command line option.
X.sp
XFollow the upgrade instructions for 5.2
X.SH PORT\ SPECIFIC
XFor various compilation options, consult the src/Makefile.
XStarting with version 5.3, a universal mailmethod was introduced: \fIsystem\fP;
XIt should be used in every system that has TCP/IP installed,
Xas it has been verified to work on
Xmany systems below -- consult the src/README file.
X.TP
XIBM Risc
XYou should use \fIsysv_ps\fP as an option in the \fIconfig\fP file,
Xand compile with xlc -D_ALL_SOURCE -qnoro. You have to use \fIsystem\fP
Xas the mailmethod.  Messages distributed to local
Xsubscribers on the system may see their messages arrive "From root...",
Xbut all other subscribers in the outside world will see "From listserver@..."
Xand/or "From list@..." which is the correct header. If you use
X\fIenv_var\ LOGNAME\ /bin/rmail\fP instead, the behavior will be reversed.
X\fItelnet\fP may not be used as a mailmethod. Compile with -DHAVE_SELECT_H,
X-DHAVE_ULIMIT_H and -DHAVE_SETJMP_H, and link with -lbsd.
XIn defs.h the system is defined as having an SVR3 flavor.
X.TP
XSGI
XYou have to use the \fIsystem\fP mailmethod.
XAll Irix versions to date seem to have a serious problem with telnet (it
Xcorrupts file descriptors). Use the \fIsysv_ps\fP option.
XIn the Makefile, define the symbol CC to be 'cc -ansi' or 'cc -D__STDC__',
Xand compile with -DHAVE_SETJMP_H and -D_POSIX_SOURCE.
XIn defs.h the system is defined as having an SVR3 flavor.
X.TP
XSUN
X\fIsystem\fP and \fItelnet\fP work fine as mailmethods.
XUse the \fIbsd_ps\fP option. The server user id should not be the same
Xas root's.
XIf you use the native cc compiler you will need to convert to K&R C
X(by using the unproto system -- consult the src/Makefile);
Xotherwise /usr/lang/acc -w (ANSI C) can be used. Compile with -DHAVE_SETJMP_H.
XIn defs.h the system is defined as having a BSD flavor. On Solaris,
Xcompile with -Dsvr4. If using the live part of the system and your host
Xis a YP/NIS client you should put the new service in the master's services
Xmap, or if on the master, remake the map. Older versions of SUNOS have
Xproblems with their include files, and in this case you have to comment out
Xthe line:
X.sp
X#include <netdb.h>
X.sp
Xin src/*.c
X.TP
XDECstations
XYou have to use \fIsystem\fP as the mailmethod; if you use
X\fItelnet\fP instead, you may have to use the \fIbad_telnet\fP
Xoption; \fIbsd_ps\fP should be used. Ultrix 3.0 has lots of system bugs
Xand you may experience strange behavior; the file locking mechanism
Xover NFS appears to be non-standard and is disabled by default; upgrade to 4.2A
Xis recommended.
XAn ANSI C compiler should be used. If you are using the unproto system,
Xuse the -nocpp flag to cc when compiling. Compile with -DHAVE_SETJMP_H.
XIn defs.h the system is defined as having a BSD flavor. All Bourne shell scripts
Xmay need to be altered to invoke /bin/sh5, before they are used.
X.TP
XConvex
XIt is suggested that you use the \fIstds\fP script (available via anonymous ftp
Xfrom cs.bu.edu) and the non-ANSI compiler; \fIstds\fP should be used as follows:
X.sp
Xstds src/nonansi/*.h src/*.c
X.sp
XThis should be done after redefining the HOMEDIR entry in src/Makefile and
Xrunning setup to make all the necessary changes. Compiling under
XANSI standards (-std flag) is not recommended, since system
Xinclude files are not ANSI-C compliant. The system cannot go interactive
Xsince the system does not support semaphores. \fIsystem\fP works fine as
Xa mailmethod. \fIpaste(1)\fP is missing and \fIstatistics\fP requests will
Xlook erroneous.
X.TP
XStardent GS series
X\fIsystem\fP and \fItelnet\fP work fine as mailmethods.
XUse the \fIsysv_ps\fP option. The unproto system has to be used (consult
Xthe src/Makefile) if not using an ANSI C compiler. The system cannot
Xgo interactive due to a system() bug. In defs.h the system
Xis defined as having an SVR3 flavor.
X.TP
XStardent/KPC Titan series
X\fIsystem\fP and \fIenv_var\ LOGNAME\ /bin/rmail\fP work fine as mailmethods.
XUse the \fIsysv_ps\fP option. The system cannot go interactive due to the
Xabsence of waitpid(), and compiling and linking with -43 (to use wait3())
Xhangs \fIserverd(1)\fP and the other applications.
XIn defs.h the system is defined as having an SVR3 flavor.
X.TP
XNeXT
X\fItelnet\fP, \fIsendmail\fP, \fIsystem\fP (if TCP/IP is installed and SMTP is
Xused) work fine as mailmethods; however, 'mailmethod sendmail
X/usr/lib/sendmail -ba' has been characterized as a "DISASTER in terms of
Xhogging the cpu." Compile with
X-D__NeXT__ if your compiler does not define this symbol (under version 3.0
Xof the OS this symbol is automatically defined -- 3.0 is the only supported
Xenvironment). Compile with -I/usr/include/bsd and -I/usr/include/bsd/sys.
XFile locking is turned off because of an OS bug.
XIn defs.h the system is defined as having a BSD flavor. The system
Xcannot go interactive due to lack of semaphore support.
X.TP
XHP-UX
XCompile with -Aa, -D_HPUX_SOURCE, -DHAVE_ULIMIT_H, -DHAVE_SETJMP_H.
X\fIsystem\fP is the mailmethod to use.
XIn defs.h the system is defined as having a BSD flavor.
X.TP
XSCO
XCompile with -Dsco and possibly with -I/usr/include/netinet.
XYou may wish to link with a
Xspecial libbsd.a which can be obtained from /pub/sco-ports/libbsd on
Xdribble.c-mols.siu.edu (131.230.93.2). You need to link with -lsocket.
XIn defs.h the system is defined as having an SVR3 flavor. The port was done
Xunder SCO version 3.2.4. The interactive part seems to hang for no apparent
Xreason.
X.TP
XApollo
XUse the \fIsystem\fP mailmethod. Do not compile with -A ansi, or if you do
Xalso compile with -Dapollo. The OS that the port was performed under was
XSR10.4 and version 6.8 of cc. The server id's path should include
X/sys5.3/usr/bin so that 'paste' may be found. In defs.h the system is defined
Xas having a BSD flavor. The system cannot go interactive due to lack of
Xsemaphore support.
X.TP
XXenix
XCompile with -Dxenix; grep(1) is buggy and is suggested that another version
Xis used. In defs.h the system is defined as having an SVR3 flavor.
X.TP
XSequent
XCompile with gcc -fwritable-strings and -Dsequent if the compiler does not
Xdefine this symbol. In defs.h
Xthe system is given a BSD flavor. If under Dynix/PTX, compile with -Dsvr3
X(PTX is a a System V R3.2 implementation; V2.0.3 includes an ANSI C compiler).
X.TP
XData General
XCompile using gcc -fwritable-strings; in defs.h the system is given an SVR4
Xflavor and is advisable not to give it a BSD one; keep in mind that
Xsignals frequently get lost.
X.TP
Xi860
XThis is a standard SVR4.2 UNIX; \fIsystem\fP works fine as mailmethod. All
Xfeatures are functional.
X.TP
Xi386
XThis is a BSD environment; the system cannot go interactive due
Xto lack of semaphore support, and file locking is turned off. \fIsystem\fP
Xworks fine as mailmethod. Compile with the -fwritable-strings flag.
X.TP
XOSF
XThis is a standard SVR3 UNIX; \fIsystem\fP works fine as mailmethod. All
Xfeatures are functional. Compile with the -std1 flag. Do not compile with
X-DHAVE_SELECT_H because of missing system header files.
X.TP
XOther
XFirst, attempt compiling with -Dunknown_port.
X.SH LIMITATIONS
X\-
XUp to 10 mailing lists can be supported (configurable in
Xsrc/defs.h and src/Makefile).
X.sp
X\-
XThe system assumes a length of 32 bits or higher for long integers.
X.sp
X\-
XA list alias may not be called "server".
X.SH COMPILER
X.TP
Xgcc
XIf you are using gcc to compile, use the -fwritable-strings flag. Versions
Xprior to 2.2.1 may cause problems.
X.SH MAILER
X.TP
XZmailer
XIf you are using the Zmailer to send mail, or any other mailer (like Smail)
Xthat requires a 'HELO hostname' to start the SMTP transaction,
Xcompile with -DZMAILER (also see src/README and src/Makefile).
X.SH REQUIREMENTS
XThe $path for the server account should include all necessary paths to
Xcut(1), paste(1), awk(1), grep(1), uptime(1) and telnet(1).
XIn addition, uptime(1) should report the number of users and the system
Xload; otherwise \fIserverd\fP(1) cannot be run with the -l option, and
X\fIlistserv\fP(1) cannot be run with the -r option. The output from uptime
Xshould look like:
X.nf
X.sp
X12:45pm up 5 days, 16 mins, 4 users, load average: 0.00, ...
X.sp
X.fi
XAlso, make sure that when posting, /usr/lib/news/\fIinews\fP exists either
Xas itself or as a link to wherever \fIinews\fP resides (also see src/Makefile).
X.sp
XFinally, grep(1) should support the -i (case insensitive matching) and
X-v (print all lines that do not match the expression) flags.
X.SH BUGS
X.TP
Xuptime
XWhen user and load restrictions are enforced, the system assumes
Xthat uptime outputs a line similar to the one shown above, 
Xin which the number of
Xusers is the third column (columns are separated by commas) and
Xthe load average is the fourth column. The problem is that on a system
Xthat has been up for less than one day, uptime will report the number
Xof users in the second column, and the load average in the third
Xcolumn. In this case, such restrictions should not be enforced.
X.TP
Xblanks
XAlthough the system handles email addresses with blanks in them fairly
Xwell, \fIstatistics\fP, \fIrecipients\fP and \fIset\ list\fP (status
Xquery) requests will show erroneous results for these users.
X.TP
Xspecial characters
XThe system does not accept addresses that contain single quotes, back-quotes
Xor wild characters.
X.TP
Xmessages exceeding limits not caught
XWhen all subscribers of a list have set their mail mode to \fIdigest\fP,
Xmessages are not subject to size limitations.
X.TP
Xno \fIstatistics\fP request for NeXT 2.1 hosts
XDue to the absense of \fIcut(1)\fP the \fI.stats\fP script has to be
Xmodified to replace the use of \fIcut(1)\fP with \fIawk(1)\fP.
X.TP
X\fIserverd\fP(1) dies with 'no tty' error message
XIn the \fIconfig\fP file use the -e flag in the \fIlist\fP, \fIserver\fP and
X\fIserverd\fP definitions followed by '> /dev/null 2>&1'. For example:
X.sp
X.ce
Xserverd -i 600 -e > /dev/null 2>&1
X.TP
Xincorrect auto-splitting of files on the way out
XWhen splitting files that exceed the specified limit, the system precalculates
Xthe number of parts it will send and puts this number in the Subject: line.
XHowever, since each part may actually be less than that limit (each part
Xextends to the nearest newline without exceeding the limit) it is possible to
Xsend more parts than predicted, if the total number of bytes backtracked (for
Xall parts) is an integer multiple of the limit; in this case the number of
Xextra parts will be this integer multiple. The probability of this happening
Xincreases as the limit is decreased.
X.SH SEE\ ALSO
Xcatmail(1), farch(1), iul(1), list(1), listserv(1), queue(1), serverd(1),
Xstart(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/server.1-*-*
echo x - doc/serverd.1
sed 's/^X//' >doc/serverd.1 <<'*-*-END-of-doc/serverd.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH serverd 1 "UNIX ListServer"
X.SH NAME
X\fBserverd\fP \- UNIX ListServer system daemon
X.SH SYNOPSIS
X\fBserverd\fP [\fB-1\fP] [\fB-e\fP] [\fB-l load\fP] [\fB-i duration\fP]
X.SH DESCRIPTION:
X\fIserverd\fP silently looks for any new messages for the request
Xserver and for any of the supported mailing lists, checks whether
Xthe time has arrived for processing the batch queue, whether
Xdigest time has been reached for the various lists, and whether any
Xlive TCP/IP connections are requested. It then spawns
X\fIlistserv\fP(1) and/or \fIlist\fP(1) as necessary, and sleeps until the
Xmessage(s) is (are) processed. \fIserverd\fP reads the \fIconfig\fP file
X(see \fIserver\fP(1)).
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-1
XExecute only once -- to be used with cron(1).
X.TP
X-e
XEcho reports to the screen; it has no effect if the system has been compiled
Xwith -DSYSLOG.
X.TP
X-l load
XEnforce load restrictions; \fIserverd\fP will postpone spawning if the
Xsystem load is above the limit specified.
X.TP
X-i duration
XA child process is also spawned which listens for live TCP/IP connections
Xat port 372 (this is referred to as the Listsener process, and its parent as
Xthe Master); for each such connection a child process is created to
Xprocess the live requests (see the INTERACTIVE\ LISTSERVER section below). The
Xmaximum \fIduration\fP time is given in seconds.
X.SH INTERACTIVE\ LISTSERVER
XProvided that the host supports the TCP/IP protocol, the system provides
Xa mechanism for processing live requests by specifying the -i flag to
X\fIserverd\fP. Any user may use \fItelnet(1)\fP to connect to a host
Xthat runs this system at port number 372, issue requests as he would
Xby email, and receive responses immediately. However, the proper way
Xto connect to the server is by using \fIiul(1)\fP which lets the user download
Xfiles through \fIget\fP requests, and provides other amenities as well.
X.PP
XWhat requests are allowed
Xdepends on the privileges assigned upon login: the user is asked for
Xan email address (default is no email address); then a password should be
Xprovided. If the user needs manager privileges, he has to provide both the
Xmanager's email address and the system's password as they appear in the
X\fIconfig\fP file. If he wishes owner privileges, he has to provide both
Xhis email address as it appears in the \fIowners\fP file and his list's
Xpassword as it appears in the \fIconfig\fP file. To gain subscriber privileges
Xhe has to provide his email address as subscribed to the list where privileges
Xare to be granted to, as well as the password \fIset\fP for that list.
X.PP
XA mismatch or no email address reduces privileges to a minimum. These
Xare restricted to \fIhelp\fP, \fIinformation\fP, \fIrecipients\fP and
X\fIstatistics\fP for nonprivate lists, \fIlists\fP, \fIindex\fP, \fIget\fP,
X\fIrun\fP and \fIrelease\fP requests. Subscriber privileges extend
Xthese to \fIset\fP, \fIrun\fP, \fIunsubscribe\fP and \fIwhich\fP requests.
XOwners are granted permission to also issue \fIsystem\fP, \fIreports\fP,
X\fIedit\fP, \fIput\fP, \fIapprove\fP and \fIdiscard\fP requests in
Xaddition to the ones above. Finally, the \fImanager\fP may issue any
Xrequest except \fIexecute\fP.
X.PP
XRequests for remote lists are serviced by attempting to connect to the
Xremote servers serving these lists.
X.PP
XThe system may restrict connections to certain hosts, and/or reject connections
Xfrom certain hosts. First, the system looks for the file \fIpriv.hosts\fP
Xwhich lists hostnames and/or IP addresses that are allowed access; only
Xhosts listed in this file are granted acces. For example, to allow access to
Xhosts on one's local network only, one could do:
X.sp
X.ce
Xln -s /etc/hosts HOMEDIR/priv.hosts
X.sp
XNext, the file \fIunwanted.hosts\fP is looked up, and connections from hosts
Xlisted in it are rejected. Regular expressions may be used in both files.
X.sp
XThe duration of each connection is limited to the number of seconds
Xgiven as argument to the -i flag. A maximum of 5 simultaneous connections
Xcan be accepted at any time. All requests are serialized and the server may
Xbe kept busy if it has not finished sending data to a socket (for example,
Xwhen a user pipes the output he receives to more(1) and the amount of data 
Xto be sent is more than the the socket's buffer).
X.PP
XTo set up your system to accept such connections, the following line has to
Xbe inserted into your host's /etc/services file:
X.sp
X.ce
Xulistserv   372/tcp
X.sp
XThis port is privileged, therefore \fIserverd\fP has to be started with
Xroot privileges (the \fIsetup\fP script takes care of this). If you do not
Xhave root access you may choose any other port number above 1024, and in
Xthis case \fIserverd\fP will give a warning every time it starts, which
Xcan be ignored.
X.PP
XConsult the src/README file for qualification tests for your system to go
Xinteractive and the Makefile for compilation options; also see the
XPORT\ SPECIFIC section in \fIserver\fP(1). Lastly, the file HOMEDIR/welcome.live
Xis copied upon log in, and it may be modified.
X.PP
XIf your setup uses a name server you may wish to link with libraries that
Xprovide DNS support.
X.SH SEE\ ALSO
Xiul(1), list(1), listserv(1), queue(1), server(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/serverd.1-*-*
echo x - doc/start.1
sed 's/^X//' >doc/start.1 <<'*-*-END-of-doc/start.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH start 1 "UNIX ListServer"
X.SH NAME
X\fBstart\fP \- start/stop the UNIX ListServer system
X.SH SYNOPSIS
X\fBstart\fP [\fB-c\fP] [\fB-r\fP] [\fB-k\fP]
X.SH DESCRIPTION
X\fIstart\fP is used to start or stop the UNIX ListServer system.
XWhen starting the system, any
Xprevious server processes still running are killed, all necessary
Xlock files are created, any previous reports are archived into files with
Xextension \fI.acc\fP, new files and directories are created for any
Xnew mailing lists, and \fIserverd\fP(1) is spawned. \fIstart\fP reads the
X\fIconfig\fP file (see server(1)), and should always be run from the server
Xaccount (also see \fIserver\fP(1)).
X.SH OPTIONS
XThe following command line options are recognized:
X.TP
X-c
XSuppress confirmation when killing processes or when creating new
Xmailing directories and files.
X.TP
X-k
XJust kill any old server system processes and exit.
X.TP
X-r
XRestrict reporting to stdout.
X.SH SEE\ ALSO
Xserver(1), serverd(1)
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas
XCopyright (c) 1991-93, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu
X.fi
*-*-END-of-doc/start.1-*-*
echo x - doc/catmail.nr
sed 's/^X//' >doc/catmail.nr <<'*-*-END-of-doc/catmail.nr-*-*'
X
X
X
Xcatmail(1)               USER COMMANDS                 catmail(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     ccccaaaattttmmmmaaaaiiiillll - append incoming mail  to  UNIX  ListServer  system
X     files
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     ccccaaaattttmmmmaaaaiiiillll {<----LLLL LLLLIIIISSSSTTTT____AAAALLLLIIIIAAAASSSS [----mmmm]> | <----rrrr>} [----ffff]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _c_a_t_m_a_i_l is used to redirect incoming mail to the UNIX  List-
X     Server  system  to  the  appropriate files, according to the
X     flags specified. The files affected are HOMEDIR/requests  (a
X     repository  for requests), HOMEDIR/lists/*/mail (public mes-
X     sages to be distributed) and HOMEDIR/lists/*/moderated (mes-
X     sages  to  be  screened out by a moderator). _c_a_t_m_a_i_l reports
X     the user id and user name that is  currently  executing  it;
X     the  setuid  bit has to be set when installed. _c_a_t_m_a_i_l first
X     locks the output file before appending to it;  if  the  file
X     cannot  be  locked  after 3 minutes, the mail is saved under
X     HOMEDIR/lost+found or HOMEDIR/lists/*/lost+found; both  _l_i_s_t
X     and  _l_i_s_t_s_e_r_v  lock their mail files while copying them to a
X     safe place.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -L LIST_ALIAS [-m]
X          If  the  -m  flag   is   not   specified,   append   to
X          HOMEDIR/lists/_L_I_S_T__A_L_I_A_S/mail;   otherwise   append  to
X          HOMEDIR/lists/_L_I_S_T__A_L_I_A_S/moderated (see list(1)).
X
X     -r   Append to HOMEDIR/requests (overrides -L).
X
X     -f   Reformat the message in the process: all lines  in  the
X          message except the first starting with "From " are con-
X          verted  to  ">From  ".   (see  also   SYSTEM SETUP   in
X          server(1)).
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     list(1), listserv(1), server(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
*-*-END-of-doc/catmail.nr-*-*
echo x - doc/farch.nr
sed 's/^X//' >doc/farch.nr <<'*-*-END-of-doc/farch.nr-*-*'
X
X
X
Xfarch(1)                 USER COMMANDS                   farch(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     ffffaaaarrrrcccchhhh - archive files under the UNIX ListServer system
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     ffffaaaarrrrcccchhhh {[----nnnn] [----bbbb | ----BBBB] [----ssss ssssiiiizzzzeeee]  [----dddd ddddiiiirrrr]  [----pppp ppppaaaasssssssswwwwoooorrrrdddd]  [----
X     DDDD ddddeeeessssccccrrrriiiippppttttiiiioooonnnn] [----tttt ffffiiiilllleeee]}  |  {[----rrrr]}  [----aaaa aaaarrrrcccchhhhiiiivvvveeee |||| ppppaaaatttthhhh----ttttoooo----
X     aaaarrrrcccchhhhiiiivvvveeee] <ffffiiiilllleeeessss>
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _f_a_r_c_h is used to archive files  under  the  UNIX  ListServer
X     system  or  remove  files already achived, with support pro-
X     vided  for  private   archives.    Archives   reside   under
X     HOMEDIR/archives  as  subdirectories;  the  default is _l_i_s_t_-
X     _s_e_r_v_e_r, and it is also  the  master  archive.  Archives  are
X     hierarchically structured, and each archive has at least two
X     files in its directory:
X
X     INDEX
X          A list of all subarchives including itself; the  format
X          is  one  line  per archive with the archive's name fol-
X          lowed by  the  archive's  full  path,  followed  by  an
X          optional password:
X
X          archive-name full-path-to-archive-directory [password]
X
X          The first entry should  be  the  archive  itself;  each
X          first level subarchive is listed next; then each second
X          level subarchive, starting with the first  first-level-
X          archive's  subarchives,  followed  by the second first-
X          level-archive's subarchives, etc. No  sibling  archives
X          may  have the same name, although there is no such res-
X          triction for archives on different levels, or  "cousin"
X          archives.  The latter archives are distinguished by the
X          relative path needed to access them (see _s_e_r_v_e_r(_1)  for
X          more information).
X
X     DIR  A list of files available from that archive; the format
X          is one line per file with the file name followed by the
X          number of parts it may be split into, followed  by  the
X          number  of  bytes of each of the parts, followed by the
X          full path to the directory these parts  can  be  found,
X          followed  by a short descriptive message about the file
X          (optional). The descriptive message  may  span  several
X          lines;  each  line  (except the last) should end with a
X          '\':
X
X          file nparts size-of-each-part+ full-path-to-directory [description [\]]
X          [multiple description lines [\]]*
X
X          The number of parts the file is split into may be -1 to
X          indicate that this is a binary file.
X
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xfarch(1)                 USER COMMANDS                   farch(1)
X
X
X
X     _f_a_r_c_h reports on  the  action  taken  on  each  input  file:
X     whether  it  was  split (and how many parts), whether it was
X     uuencoded, whether all files have been tar'red into a single
X     one,  the  archive  this  file  was  archived under, and the
X     directory the output file(s) (if any) has/have been  placed.
X     It also reports when the input files are tarred.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -n   Do not split files when archiving them. The default  is
X          to split them with each part not larger than the speci-
X          fied size (see the -s option below).
X
X     -b   Input files  are  binary;  they  are  uuencoded  before
X          archived.
X
X     -B   Input files are binary; they are neither uuencoded  nor
X          split.
X
X     -s size
X          Specify the maximum _s_i_z_e in kilobytes of  each  of  the
X          subparts (default is 64).
X
X     -d dir
X          Specify the _d_i_rectory that the output files are  to  be
X          placed;  if  left  out,  it  defaults  to the specified
X          archive's directory (HOMEDIR/archives/listserver if the
X          archive  is  left  out as well).  If the directory (and
X          all of its subdirectories, if any) does not  exist,  it
X          will be created.
X
X     -p password
X          When a new archive is to be created, and  that  archive
X          is to be private, specify the access password.
X
X     -D description
X          Put  _d_e_s_c_r_i_p_t_i_o_n  (most  likely  surrounded  by  single
X          quotes)  in  the  DIR file as explanatory comment about
X          the archived file. This option does not make much sense
X          when archiving more than one file at the same time.
X
X     -t file
X          Input  files  are  tar'red  into  _f_i_l_e  which  is  then
X          archived  (the  -b  flag  is  automatically turned on).
X          Turning on the -B flag will prevent uuencoding  of  the
X          tar  file.  Note that whatever the path to _f_i_l_e may be,
X          it will be moved to the specified directory  as  speci-
X          fied by the -d flag.
X
X     -r   Remove the specified file(s) from the specified archive
X          (see below), or the default one; it has higher priority
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xfarch(1)                 USER COMMANDS                   farch(1)
X
X
X
X          than all of the options above.
X
X     -a archive | path-to-archive
X          Specify the  _a_r_c_h_i_v_e  that  the  input  files  will  be
X          archived  under  (default  is  listserver). Archives on
X          different levels of the hierarchy  may  have  the  same
X          name  and  in this case a _p_a_t_h-_t_o-_a_r_c_h_i_v_e may be speci-
X          fied to distinguish between them;  _p_a_t_h-_t_o-_a_r_c_h_i_v_e  has
X          the   form  _a_r_c_h_i_v_e[/_a_r_c_h_i_v_e[/_a_r_c_h_i_v_e...]]   (see  also
X          server(1)). If any of the archives in the path  do  not
X          exist, they will be created with the necessary files.
X
X     File names in the DIR file have to be unique and _f_a_r_c_h  will
X     not archive duplicate files.
X
XAAAARRRRCCCCHHHHIIIIVVVVIIIINNNNGGGG AAAA FFFFIIIILLLLEEEE
X     _f_a_r_c_h by default splits the input files if necessary (or the
X     tar  file, if any), into files of maximum size as specified.
X     Each part will contain as many complete lines from the  ori-
X     ginal  input  file as possible, without exceeding the speci-
X     fied size. Binary files (including the tar file)  are  uuen-
X     coded  before  they  are  archived  unless  the -B option is
X     specified, and all archived files are compressed, if  possi-
X     ble.
X
XAAAADDDDDDDDIIIINNNNGGGG AAAA NNNNEEEEWWWW AAAARRRRCCCCHHHHIIIIVVVVEEEE
X     automatically
X          Simply specify the new archive as argument  to  the  -a
X          flag.  All necessary DIR and INDEX files as well as all
X          required directories will be created,  and  all  parent
X          archives will be updated.
X
X     by hand
X          Step 1
X
X          Create a directory under HOMEDIR/archives or  under  an
X          existing  archive's  directory (beware of the hierarchy
X          structure), naming the  directory  after  the  archive.
X          The  archive's name should be unique among its siblings
X          on that level in the hierarchy (besides, mkdir will not
X          let specify a name that already exists).
X
X          Step 2
X
X          Edit    the     master     archive's     index     file
X          (HOMEDIR/archives/listserver/INDEX)  and  add  the  new
X          archive along with its path (and optional  password  --
X          see below) at the proper place in the file, making sure
X          you adhere to the rules about hierarchy outined  above.
X          The  first  line  of  every  INDEX  file  should be the
X          archive itself.  Also  edit  (add  to)  every  ancestor
X          archive's  INDEX  file,  if  the new archive is not the
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xfarch(1)                 USER COMMANDS                   farch(1)
X
X
X
X          default, again making  sure  you  adhere  to  the  same
X          rules.
X
X          Step 3
X
X          Create a new INDEX file in the new archive's  directory
X          and put an entry for itself.
X
X          Step 4
X
X          Create an empty DIR file in the  new  archive's  direc-
X          tory.
X
XPPPPRRRRIIIIVVVVAAAATTTTEEEE AAAARRRRCCCCHHHHIIIIVVVVEEEESSSS
X     Private archives are archives that require  a  password  for
X     obtaining indices and/or files from them. To make an archive
X     private, simply append a password  (case  does  not  matter)
X     after  its  full  path specification in every INDEX file the
X     archive is defined (its own plus all ancestors' -- the  same
X     password  has  to be used in all of these files). Then, that
X     password has to be made known to all users  who  are  to  be
X     granted  access to this archive.  Note that all files in the
X     same private archive can be obtained with  the  same  single
X     password.
X
XEEEEXXXXAAAAMMMMPPPPLLLLEEEESSSS
X     Archive src/data and src/data2 under archive listserver (the
X     default),  using  a  maximum  file  size  of  1K; the output
X     file(s) will be placed in /tmp/tmp:
X
X       % farch -s 1 -d /tmp/tmp src/data src/data2
X
X     Archive   /etc/hosts   under   archive   unix    (that    is
X     listserver/unix)  --  the  -n  flag is used to avoid writing
X     split parts to /etc which is doomed to fail:
X
X       % farch -n -a unix -d /etc -p private /etc/hosts
X
X     Archive  /etc/password  under  archive  pub/unix  (that   is
X     listserver/pub/unix):
X
X       % farch -n -a pub/unix -d /etc /etc/passwd
X
X     Tar and archive all files in /usr/src to the default archive
X     and place the tar file under /tmp/tmp:
X
X       % farch -t HOMEDIR/source -d /tmp/tmp /usr/src/*.c
X
X     Descriptive messages about these files  are  added  manually
X     into  the  archive's DIR file. However, the -D option can be
X     used to specify a string as follows:
X
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
X
X
X
Xfarch(1)                 USER COMMANDS                   farch(1)
X
X
X
X       % farch -D 'UNIX ListServer system files' -t ulistserv.tar -n -d /usr/server /usr/server/*
X
XUUUUPPPPGGGGRRRRAAAADDDDIIIINNNNGGGG
X     If you are upgrading from:
X
X     5.5  The -B, -p, -r and -D options are new; functionality is
X          the same.
X
X     5.41 or less
X          Every DIR file should now include the size of  each  of
X          the  parts  of every file, placed between the number of
X          parts and the path name.
X
X          The -a option has been  extended  to  accept  paths  to
X          archives.
X
XRRRREEEESSSSTTTTRRRRIIIICCCCTTTTIIIIOOOONNNNSSSS
X     - Archive names and input files must  use  only  lower  case
X     characters of the alphabet.
X
XWWWWAAAARRRRNNNNIIIINNNNGGGGSSSS
X     - Input files that are to be  tar'red  should  only  include
X     relative  path names; otherwise the end user may not be able
X     to extract them.
X
X     - The tar file should not be any of the input files.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     server(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               5
X
X
X
*-*-END-of-doc/farch.nr-*-*
echo x - doc/iul.nr
sed 's/^X//' >doc/iul.nr <<'*-*-END-of-doc/iul.nr-*-*'
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     iiiiuuuullll - Interactive UNIX ListServer client
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     iiiiuuuullll [----vvvv] [----tttt ttttiiiimmmmeeeeoooouuuutttt] [----bbbb bbbbuuuuffffffffeeeerrrr----ssssiiiizzzzeeee] hhhhoooosssstttt [ppppoooorrrrtttt]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _i_u_l is a  client  that  connects  to  the  specified  _h_o_s_t's
X     Interactive UNIX ListServer port (372) for a live session --
X     i.e. for processing  requests  live.  Another  _p_o_r_t  may  be
X     specified  if  necessary. The _h_o_s_t may be a host name, or an
X     IP address.
X
X     Upon connection establishment, the protocol attempts to  set
X     the  user's  privileges  during the session by requesting an
X     email address and a password.  These are used  to  determine
X     whether  the user is to be granted system, owner, subscriber
X     or casual user privileges. If the user  provides  his  owner
X     email  address  and  his list's password, he will be granted
X     owner privileges. If he provides an email  address  that  he
X     uses  for subscription to a list, along with the password he
X     uses  for  that  list,  he  will   be   granted   subscriber
X     privileges. The system provides a brief listing of all valid
X     requests the user may issue during the session. If no  email
X     address  is  provided,  or no matches are found, the user is
X     restricted to a few requests.
X
X     Requests for remote lists are serviced by attempting to con-
X     nect  to  the  IUL  server(s) (if any) of the system(s) that
X     handle those lists.
X
X     The IUL server recognizes the following special requests:
X
X     quit/exit
X          End the session.
X
X     ?/privileges
X          Get a brief listing of all valid requests.
X
X     timeleft
X          Prints the remaining time in seconds.
X
X     &<new-line>
X          Input continues on the next line.
X
X     binary
X          Switch to binary mode when transferring files.
X
X     ascii
X          Switch to ASCII mode when transferring files.
X
X     < filename
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X          Input is taken from the specified file. Each line  will
X          be  interpreted  as a separate request, unless the file
X          is specified in conjunction with a _p_u_t request. If  the
X          '<' is to be used literally it must be escaped with '\'
X          or enclosed in quotes.
X
X     > filename
X          Redirect the reply to  the  request  to  the  specified
X          file. Error messages (such as rejections due to invalid
X          requests, etc.) are not redirected.   When  a  file  is
X          downloaded  via  a  _g_e_t request, this will override the
X          file name that will be saved under. If the '>' is to be
X          used literally it must be escaped with '\', or enclosed
X          in quotes.
X
X     >> filename
X          Same as above, but the reply is appended to the  speci-
X          fied file.
X
X     | prog [args]
X          The output of the request is piped  to  _p_r_o_g;  this  is
X          similar to a UNIX pipe. _p_r_o_g may be any valid UNIX com-
X          mand, including other pipes,  file  redirections,  etc.
X          Since  '<'  has  higher precedence in this context, you
X          should escape any '<' characters intended to be used by
X          the  pipe,  otherwise  the  system  will assume you are
X          feeding it batched requests. Quotes may also be used to
X          protect these characters.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following options are recognized:
X
X     -v   Turn verbose mode on; the server reply codes are echoed
X          along with predetermined messages.
X
X     -t timeout
X          The default time out  for  a  server  response  is  180
X          seconds;  to reset use the -t flag. This timeout should
X          not be confused with the remaining time  of  a  connec-
X          tion.
X
X     -b buffer-size
X          The default socket buffer size is 8K; to reset use  the
X          -b flag (the argument specifies kilobytes).
X
X
XUUUUSSSSEEEERRRR PPPPRRRRIIIIVVVVIIIILLLLEEEEGGGGEEEESSSS
X     Casual users may only issue  help,  information,  recipients
X     and  statistics  for  nonprivate  lists,  lists, index, get,
X     search and release  requests.   Subscriber  privileges  also
X     include  the set, run, unsubscribe and which requests.  Own-
X     ers may, in addition,  issue  all  of  their  administrative
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X     requests.
X
X     Issue a 'help live' request when you first connect to an IUL
X     server.
X
XRRRREEEESSSSTTTTRRRRIIIICCCCTTTTIIIIOOOONNNNSSSS
X     The connection  duration  is  limited  to  a  server-imposed
X     limit.  After that, a connection may be broken by the server
X     as necessary.  A connection will  not  be  broken  during  a
X     transfer.
X
XEEEEXXXXAAAAMMMMPPPPLLLLEEEESSSS
X     request> put ermis ermis1 subscribers </usr/server/backup/lists/ERMIS/subs >out
X
X     request> get listserver example.dat 1 3 >> out
X
X     request> get listserver example.dat
X
X     request> index iul > IUL
X
X     request> < /tmp/batched.requests
X
X     request> index | more
X
X     request> lists | cut -d ' ' -f1,2 | more
X
X     request> search iul "\>\> out"
X
X     In this last example, we escape '>' to protect it from being
X     interpreted  as  a regular expression separator, and enclose
X     the whole pattern in quotes to protect the '>>'  from  being
X     interpreted  as  the append operator. The same request could
X     have been written as:
X
X     request> search iul \\>\\> out
X
XFFFFIIIILLLLEEEESSSS
X     iul.c, iul.h
X          Source code for  the  client.  See  iul.c  for  compile
X          options.
X
X     iulp.h
X          Definition of the Interactive UNIX ListServer Protocol.
X
X     makefile
X          makefile to build _i_u_l. _i_u_l is  written  with  BSD-style
X          signal handling, therefore on some hosts (like IBM AIX)
X          you will have to link with BSD versions  of  signal(2),
X          socket(3N),  and  fcntl(2).  Platforms  that  have  the
X          <sys/select.h> and <ulimit.h> header files should  com-
X          pile  with  -DHAVE_SELECT_H and/or -DHAVE_ULIMIT_H. SCO
X          ports should  compile  with  -Dsco.  Always  link  with
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X          libraries  that  provide  DNS  support (resolver linked
X          in).
X
XBBBBUUUUGGGGSSSS
X     Please report any bugs or enhancements to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
*-*-END-of-doc/iul.nr-*-*
echo x - doc/list.nr
sed 's/^X//' >doc/list.nr <<'*-*-END-of-doc/list.nr-*-*'
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     lllliiiisssstttt - process a specified UNIX ListServer mailing list
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     lllliiiisssstttt ----LLLL <LLLLIIIISSSSTTTT____AAAALLLLIIIIAAAASSSS> [----1111] [----eeee] [----ssss] [----pppp]  [----PPPP]  [----mmmm ####]  [----ffff]
X     [----rrrr] [----MMMM] [----dddd] [----iiii <aaaaddddddddrrrreeeessssssss>] [----vvvv] [----DDDD]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN::::
X     _l_i_s_t distributes the  message(s)  sent  to  _l_i_s_t__a_l_i_a_s@_y_o_u_r-
X     _d_o_m_a_i_n;  the  file  ".ignored" in the list's subdirectory is
X     used to filter out any unwanted messages (see below).
X
X     Messages from mailer daemons are  forwarded  to  the  list's
X     owner  and  screened  looking  for delivery errors (in which
X     case appropriate action is taken -- users are either immedi-
X     ately  removed  from the list, or after a series of delivery
X     errors within a certain time frame -- entries  removed  from
X     the       system's      files      are      placed      into
X     HOMEDIR/lists/*/removed.users                            and
X     HOMEDIR/lists/*/removed.alias  files), whereas messages from
X     non-subscribers are returned to the original senders (the -f
X     flag overrides this). Messages whose first line looks like a
X     ListServer request are bounced back to the sender.
X
X     Messages from news groups are distributed only to local sub-
X     scribers and peer lists. Messages from peers are distributed
X     to local subscribers and possibly  posted  to  news  groups.
X     Finally,  messages from local subscribers are either distri-
X     buted locally, copies are sent to all peers, and are  possi-
X     bly  posted  to  news groups, or are forwarded to the list's
X     owner for screening if the list is moderated.
X
X     All distributed messages (i.e. legitimate messages from sub-
X     scribers,  peers and news groups -- not rejected, ignored or
X     error    messages)    are    automatically    archived    in
X     HOMEDIR/lists/*/archive.     In    contrast,    the    files
X     HOMEDIR/lists/*/mbox contain  all  messages  sent  to  these
X     lists (including, error, rejected and ignored messages).
X
X     Email from one or more subscribers may be  selectively  dis-
X     tributed  to an alternate list of recipients, by way of res-
X     tricted mail (see below), in which case  mail  will  not  be
X     distributed to the regular subscribers.
X
X     When the system is for some reason aborted  while  making  a
X     delivery,  a built-in mechanism allows it to resume from the
X     point it left off.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -L LIST_ALIAS
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X          Process  any  messages  sent  to  this  _L_I_S_T__A_L_I_A_S   --
X          _L_I_S_T__A_L_I_A_S should be in capital letters.
X
X     -1   Execute only once; process the mailing list and  return
X          control  to  _s_e_r_v_e_r_d(1); any new messages that may have
X          arrived in the meantime will be processed  at  a  later
X          time.  Without  this option, _l_i_s_t will be listening for
X          messages for the specified list  for  ever.  _s_e_r_v_e_r_d(1)
X          uses this option by default when spawning _l_i_s_t.
X
X     -e   Echo reports to the screen; it has  no  effect  if  the
X          system has been compiled with -DSYSLOG.
X
X     -s   By default, only subscribers can  send  messages  to  a
X          list. This option turns off subscription checking.
X
X     -p   By default, replies to messages posted to  news  groups
X          go  to  the list; this option forces replies to be for-
X          warded to the original author.
X
X     -P   By default, replies to messages sent to subscribers and
X          peers  go to the list; this option forces replies to be
X          forwarded to the original author.
X
X     -m number
X          Normally, each outgoing message has one recipient. This
X          flag  switches to multi-recipient outgoing messages and
X          specifies the _n_u_m_b_e_r of recipients to  be  included  in
X          these messages.
X
X     -f   Forward any messages from non-subscribers to the list's
X          owner. By default, they are returned to the sender.
X
X     -r   Restricted mail: _l_i_s_t will look  at  the  ".restricted"
X          file (see below) to get the name of the alternate reci-
X          pients file. If the sender is  listed  in  that  ".res-
X          tricted"  file,  his  messages  will  be distributed to
X          users listed in the alternate recipients file.
X
X     -M   The list is moderated; all incoming messages  not  from
X          the  owner(s) are forwarded to the list's primary owner
X          for review and editing. The owner then sends  back  the
X          ones  that  are  approved for posting (for an alternate
X          scheme see the MODERATED LISTS section below).
X
X     -d   Force a digest to be distributed. This flag  is  inter-
X          nally  used  by  _s_e_r_v_e_r_d(1)  when  digest time has been
X          reached for this list.
X
X     -i address
X          Send a partial digest to _a_d_d_r_e_s_s (what has  accumulated
X          so  far).   This flag is internally used by _l_i_s_t_s_e_r_v(1)
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X          when the user identified by _a_d_d_r_e_s_s  changes  his  _m_a_i_l
X          mode  from _d_i_g_e_s_t to something else. Note that when the
X          time arrives for the current digest to be  distributed,
X          this user will get duplicate messages.
X
X     -v   Print version information.
X
X     -D   Turns debugging on. When the _s_y_s_t_e_m mailmethod is  used
X          (see  server(1)),  a  copy of the last SMTP transaction
X          can  be   found   in   the   files   HOMEDIR/sent   and
X          HOMEDIR/received.
X
XAAAADDDDDDDDIIIINNNNGGGG AAAA NNNNEEEEWWWW LLLLIIIISSSSTTTT
X     To add a new list, first shut the system down  by  executing
X     _s_t_a_r_t(1):
X
X       % _s_t_a_r_t -_k
X
X     Then edit the _c_o_n_f_i_g file and add a line  defining  the  new
X     list  --  you may wish to add a comment and/or disable a few
X     requests  also.   A  new  alias  has  to  be   set   up   in
X     /etc/aliases, /usr/lib/aliases or /usr/ucblib/aliases of the
X     following form:
X
X     list_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f"
X
X     Note that _l_i_s_t__a_l_i_a_s is all lower case and _L_I_S_T__A_L_I_A_S is all
X     upper case. In this case _c_a_t_m_a_i_l(1) appends incoming mail to
X     the list's mail file (HOMEDIR/lists/LIST_ALIAS/mail).   Also
X     keep  in mind the case of reformatting messages as described
X     in _c_a_t_m_a_i_l(1).
X
X     Finally,  restart  the  system.  At  this  point,  the  file
X     ".ignored"  in the HOMEDIR/lists/LIST_ALIAS directory may be
X     edited to add more unwanted senders. You  should  also  edit
X     the files ".welcome" and ".info" in the list's subdirectory;
X     the former is included in the return message for a new  sub-
X     scription  request for that list, and the latter is included
X     in the return message for an _i_n_f_o_r_m_a_t_i_o_n  request  for  that
X     list  (see  _l_i_s_t_s_e_r_v(1)).  These  files  may  be simple text
X     files, or shell scripts (see the HELP section in server(1)).
X
XAAAADDDDDDDDIIIINNNNGGGG AAAA PPPPEEEEEEEERRRR LLLLIIIISSSSTTTT
X     Peer lists are mailing lists that happen to  be  subscribers
X     to  one or more of your own mailing lists; they handle local
X     distribution of messages just like you do at your own  site.
X     Peer lists can be mutual subscribers, so that a message ori-
X     ginating in a peer list gets distributed over there locally,
X     and  a copy is sent to the other for local distribution, and
X     vice versa. An automatic mechanism is provided for  avoiding
X     loops.   A peer list can be added to a local mailing list by
X     using the script _p_e_e_r:
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X     peer <LIST_ALIAS> <remote alias> <peer address> <remote ListServer address>
X
X     where  _L_I_S_T__A_L_I_A_S  is a local list alias in capital letters,
X     _r_e_m_o_t_e _a_l_i_a_s is the peer's  alias  on  the  remote  machine,
X     _p_e_e_r _a_d_d_r_e_s_s is taken from the first line of the header of a
X     test message sent to your host by the peer (the  first  line
X     may  be something like: "From peer@other-domain" -- see also
X     the     discussion     about     aliases     below),     and
X     _r_e_m_o_t_e _L_i_s_t_S_e_r_v_e_r _a_d_d_r_e_s_s  is  the full email address of the
X     remote request-handler.
X
X     Here is an example for establishing connection  between  two
X     peer  lists a@domain1 and b@domain2:  The _m_a_n_a_g_e_r at domain1
X     issues the following command:
X
X     peer a b b@domain2 listserver@hdomain2
X
X     The _m_a_n_a_g_e_r at domain2 issues the following command:
X
X     peer b a a@domain1 listserver@domain1
X
X     Once  these  two  commands  are  issued  the  connection  is
X     automatically set up.  Peer lists should make sure that only
X     one of them posts to the same news group(s), and  that  only
X     one   of   them   receives   articles  from  the  same  news
X     group(s)/gateway(s).  Lists handled by the same server  can-
X     not be mutual peers. Finally, peer lists should not be regu-
X     lar subscribers (the _p_e_e_r  script  places  them  in  a  file
X     called ".peers").
X
XNNNNEEEEWWWWSSSS GGGGRRRROOOOUUUUPPPPSSSS AAAANNNNDDDD GGGGAAAATTTTEEEEWWWWAAAAYYYYSSSS
X     A mailing list may be linked with one or  more  news  groups
X     (or  gateways)  from which it may receive messages for local
X     distribution, and/or send messages to the  newsgroup(s)  for
X     posting  --  in  this  case  only messages from regular sub-
X     scribers and peers are sent for posting, i.e. no  news  mes-
X     sages  will  be  posted  to any news groups (or gateways). A
X     news group or gateway is linked using the script _n_e_w_s:
X
X        news <LIST_ALIAS> <news group> <email address> <mode>
X
X     where _L_I_S_T__A_L_I_A_S is a local list alias (in capital  letters)
X     that  is  being  linked to the news group, _n_e_w_s _g_r_o_u_p is the
X     name of  the  news  group  (used  only  when  posting)  e.g.
X     misc.test,  _e_m_a_i_l _a_d_d_r_e_s_s  is the address of the backbone or
X     moderator of the news group, or the gateway, and it is taken
X     from  the first line of the header of a test message sent to
X     your host by the group (the  first  line  may  be  something
X     like:  "From  gateway@foo  ..."  --  see also the discussion
X     about aliases below); _m_o_d_e is one of the following:
X
X     receive
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X          The list will only be receiving messages from this news
X          group  and  never post to it. This allows access to the
X          group (or gateway) to send articles to the list.
X
X     send_receive
X          The list may  be  receiving  messages  from  this  news
X          group, and it will post to it any messages from regular
X          subscribers and peer lists (messages from  news  groups
X          are  never  posted  to  other  news  groups). This also
X          allows access to the group (or gateway) to  send  arti-
X          cles to the list.
X
X     Of course, the news group's caretaker has to be notified  of
X     the  list's  address so that articles will indeed be sent to
X     it. Finally, news groups should not be  regular  subscribers
X     (the _n_e_w_s script places them in a file called ".news").
X
X     If the config option _p_o_s_t__m_a_i_l is used, the system will  use
X     _i_n_e_w_s  for  posting,  and  it assumes that the path to it is
X     /usr/lib/news/inews, so make sure that _i_n_e_w_s resides  there,
X     or  a  link  exists to it. In this case, the group's name is
X     used for posting (e.g. misc.test). If instead  _g_a_t_e__m_a_i_l  is
X     defined,  messages  will  be sent via email to news gateways
X     (using the _e_m_a_i_l _a_d_d_r_e_s_s) -- the _n_e_w_s _g_r_o_u_p name has no sig-
X     nificance in this case.
X
XMMMMOOOODDDDEEEERRRRAAAATTTTEEEEDDDD LLLLIIIISSSSTTTTSSSS
X     The system supports two schemes for  moderating  lists.  The
X     first  scheme  uses the -M flag to _l_i_s_t(1), and in this case
X     messages not from the list's owner(s) are forwarded  to  the
X     list's  primary  owner for review; the owner then sends back
X     the approved (and possibly edited) ones.
X
X     The second scheme uses the -m flag to  _c_a_t_m_a_i_l(1).  In  this
X     case,  the  list's  alias is changed slightly in the aliases
X     file:
X
X     list_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f -m"
X
X     _l_i_s_t(1) distributes messages from a  file  called  _m_a_i_l;  by
X     having  incoming  messages  redirected to the file _m_o_d_e_r_a_t_e_d
X     (the effect of the -m flag to _c_a_t_m_a_i_l(1)), the list's  owner
X     may  _a_p_p_r_o_v_e  or  _d_i_s_c_a_r_d  any number of these messages (see
X     _l_i_s_t_s_e_r_v(1)).  Whenever a new mail  message  arrives  for  a
X     moderated  list,  a  copy  is  sent to the list's owner with
X     instructions for approving or discarding it. These  instruc-
X     tions  include a unique tag number for identifying that mes-
X     sage. When a message is approved, it is transferred  to  the
X     _m_a_i_l file for later delivery, and when discarded, it is sim-
X     ply removed from the _m_o_d_e_r_a_t_e_d file. This scheme  cuts  down
X     on mail traffic.
X
X
X
X
XAnastasios Kotsikonas                                               5
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
XRRRREEEESSSSTTTTRRRRIIIICCCCTTTTEEEEDDDD MMMMAAAAIIIILLLL
X     When the -r flag is used with  _l_i_s_t(1),  for  every  message
X     received  its  sender  is  checked  against  a list of "res-
X     tricted" email addresses, in the file ".restricted"  in  the
X     list's  directory -- a subset of the ".subscribers" file. If
X     a match is found, mail is forwarded to the people listed  in
X     the file following this email address. If no match is found,
X     the message is distributed to the regular subscribers.  Note
X     that  the  alternate  recipients  file should be in the same
X     format as the ".subscribers".
X
X....SSSSUUUUBBBBSSSSCCCCRRRRIIIIBBBBEEEERRRRSSSS
X     The format is as follows:
X
X     One entry per line; each entry is the full email address  of
X     the  subscriber as it appears in the "From " field, followed
X     by the word "ACK" (in which case  his/her  message  will  be
X     sent  back  to  him/her as an acknowledgement), "NOACK" (the
X     opposite), "POSTPONE" (no mail will be sent until  the  user
X     changes  mode  again), or "DIGEST" (digests are periodically
X     sent), followed by a string  that  plays  the  role  of  the
X     user's  password, followed by either "YES" or "NO" which are
X     the  values  of  the  conceal  attribute,  followed  by  the
X     subscriber's name. See also _l_i_s_t_s_e_r_v(1).
X
X....RRRREEEESSSSTTTTRRRRIIIICCCCTTTTEEEEDDDD
X     The format is as follows:
X
X     One entry per line; each entry is the full email address  of
X     the   subscriber,  followed  by  a  file  name  where  email
X     addresses of recipients are listed (just like in the  ".sub-
X     scribers" file). Example:
X
X        jdoe@foo.bar.com  HOMEDIR/lists/LIST_ALIAS/.recipients
X
X     If the recipient file given is the word "NONE", then no  one
X     will receive any messages.
X
X....AAAALLLLIIIIAAAASSSSEEEESSSS
X     It   is    possible    that    a    subscriber's/peer's/news
X     group's/gateway's  email  may  arrive using a different path
X     than registered, which may  raise  subscription  issues.  To
X     work  around  this,  each list provides a ".aliases" file in
X     its  subdirectory,  which  may   contain   alternate   email
X     addresses  to  be  used  when checking for subscription. The
X     format is one line per alias with the following information:
X
X                 alias-address address-as-subscribed
X
X     Please note that only the subscribed  address  is  used  for
X     sending  out  email  (see next section about regular expres-
X     sions).
X
X
X
XAnastasios Kotsikonas                                               6
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X     The  aliased  address  may  be  a  regular  expression  with
X     egrep(1)  style  syntax, in which case the following charac-
X     ters have special meanings:  '~',  if  leading  the  regular
X     expression  it  reverses  its  meaning; '|' and '&' separate
X     multiple regular expressions (logical OR and AND);  '<'  '>'
X     group  regular  expressions  (we preserve the meaning of the
X     parentheses from ed(1), and remove the meaning of  <  and  >
X     from  ed(1)  since in the ListServer context they are either
X     the default, or inappropriate).  These can be used literally
X     by  escaping them with '\'. In addition, the following char-
X     acters should be defined in matched pairs: (), <>,  [],  "".
X     For example:
X
X                  jdoe@.*\.bar\.com jdoe@foo.bar.com
X
X     will enable this user to send messages from any  machine  in
X     his local network and receive replies at foo.bar.com -- keep
X     in mind that a '.'  matches exactly one character, and  '.*'
X     matches  zero  or  more  characters.   In a more complicated
X     example:
X
X           ~jdoe@cc.*|jdoe@....*\.bar\.com jdoe@foo.bar.com
X
X     will match if jdoe sends a message from a machine whose name
X     does  not  start with 'cc', or from a machine whose name has
X     at least 3 characters.
X
X     If   certain   parts   of   the   regular   expression   are
X     parenthesized,  then the strings that matched the subexpres-
X     sions can be used in the 'address-as-subscribed' to form new
X     return  addresses;  these matched strings are accessed by \_n
X     where _n is a digit between 1 and  9.  For  example,  if  the
X     sender is:
X
X                     GATE!HOP!USER@UUCP.SOME.COM
X
X     and the entry in the ".aliases" file reads:
X
X               [^!@]*!([^!@.]*)!([^!@]*)@.*  \2@\1.UUCP
X
X     then what will be returned as 'address-as-subscribed' is:
X
X                            USER@HOP.UUCP
X
X     This way you can map addresses coming in via  various  gate-
X     ways  to  steady  ones  (\2  refers  to  the  second  set of
X     parentheses, and  \1  to  the  first).   Managers  may  edit
X     HOMEDIR/src/regex.c  to  test  regular  expression behavior,
X     introduce ls(1) style meanings to the * and ?  wild  charac-
X     ters,  undefine ed(1) special characters, and enforce strict
X     egrep(1) syntax (remove the meanings of ~ & < and >).
X
X
X
X
XAnastasios Kotsikonas                                               7
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X     Regular expressions should be used with caution for  obvious
X     reasons.
X
X     If someone is experiencing subscription  problems,  you  may
X     wish  to add their alternate email address(es) in this file.
X     This includes regular subscribers, news  groups,  peers  and
X     gateways.  If the sender is also a restricted subscriber, do
X     not forget to put another entry in  ".restricted"  with  the
X     new  alternate address. Any number of aliases may be defined
X     for each individual address.
X
X     In addition, a ".aliases" file is also  provided  for  List-
X     server  requests  and  is  under HOMEDIR. The same syntax is
X     used, but each entry has a slightly different meaning:
X
X              address-as-arrived address-used-for-reply
X
X     In this case, _a_d_d_r_e_s_s-_u_s_e_d-_f_o_r-_r_e_p_l_y will be used  to  reply
X     to  all  requests  sent in by _a_d_d_r_e_s_s-_a_s-_a_r_r_i_v_e_d. A user may
X     have any number of aliases, but only the first one  matching
X     _a_d_d_r_e_s_s-_a_s-_a_r_r_i_v_e_d  will  be  used.  Like  before, the first
X     argument may be a regular expression and the second may  use
X     the matches in the first.
X
X     For each new list, the system  puts  the  following  default
X     alias in its ".aliases" file:
X
X                         ^@.*:(.*)@(.*) \1@\2
X
X     This removes source routing.
X
X....IIIIGGGGNNNNOOOORRRREEEEDDDD
X     As described before, the system's home directory as well  as
X     every  list's  subdirectory contains a ".ignored" file which
X     is used to filter out messages sent by  certain  users.  The
X     default  file contains entries for server, bin, and sys; you
X     may wish to add an  entry  for  every  list  alias  that  is
X     defined  on  your system. A list's ".ignored" file also con-
X     tains an entry of its alias and full email address, as  well
X     as the server account's full email address.
X
X     Entries in this file may  also  be  regular  expressions  as
X     explained  in the previous section. For example, to restrict
X     requests (and postings) to .com and .edu  domain  addresses,
X     one may add:
X
X                          ~<.*\.com|.*\.edu>
X
X     Notice, it is incorrect to list them as follows:
X
X                               ~.*\.com
X                               ~.*\.edu
X
X
X
XAnastasios Kotsikonas                                               8
X
X
X
X
X
X
Xlist(1)                  USER COMMANDS                    list(1)
X
X
X
X     as anything not from the .com domain matches the first regu-
X     lar  expression,  and  therefore will be ignored.  To refuse
X     access to certain user  names  and  certain  sites  one  may
X     include:
X
X                         .*\.bar\.com|jdoe@.*
X
X     The system's manager should use extra  caution  when  adding
X     regular expressions to the system's ".ignored" file, because
X     a simple '.*' prohibits anyone from using its services.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     catmail(1), listserv(1), server(1), serverd(1), start(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               9
X
X
X
*-*-END-of-doc/list.nr-*-*
echo x - doc/listserv.nr
sed 's/^X//' >doc/listserv.nr <<'*-*-END-of-doc/listserv.nr-*-*'
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     lllliiiissssttttsssseeeerrrrvvvv - process requests  sent  to  the  UNIX  ListServer
X     request handler
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     lllliiiissssttttsssseeeerrrrvvvv  [----1111]  [----eeee]  [----iiii]  [----nnnn]  {[----aaaa <LLLLIIIISSSSTTTT____AAAALLLLIIIIAAAASSSS>]}*   {[----
X     rrrr <rrrreeeeqqqq>]}* {[----dddd <rrrreeeeqqqq>]}* {[----bbbb <rrrreeeeqqqq>]}* [----BBBB] [----DDDD]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _l_i_s_t_s_e_r_v processes user requests  sent  to  _l_i_s_t_s_e_r_v_e_r@_y_o_u_r-
X     _d_o_m_a_i_n,  forwards  requests  about  known  remote lists, and
X     processes list-owner administrative requests (see  also  the
X     LIST OWNERS section below).  Each request should appear in a
X     separate  line  with  any  possible  arguments.   The   file
X     ".ignored"  is used in the system's home directory to filter
X     out unwanted senders. If the system  is  not  instructed  to
X     ignore   invalid   requests   (see  the  CONFIG  section  in
X     server(1)), the sender is  notified  of  the  first  invalid
X     request; all subsequent requests are ignored.  For each suc-
X     cessfully completed request, a confirmation is sent back  to
X     the sender.
X
X     _l_i_s_t_s_e_r_v stops  reading  requests  when  it  encounters  the
X     string  "--" in a line by itself, which on most systems sig-
X     nifies the start of the .signature message, or at the  first
X     occurence  of  a thankful request. _l_i_s_t_s_e_r_v reads the _c_o_n_f_i_g
X     file (see server(1)).
X
X     Subscriptions may be manager-approved (private lists), files
X     and  archive  indices  may  be  password  protected (private
X     archives), and requests may be placed in a batch  queue.   A
X     single  request  may  span  multiple lines if each part ends
X     with &\_n.
X
XUUUUSSSSEEEERRRR RRRREEEEQQQQUUUUEEEESSSSTTTTSSSS
X     The following user requests are recognized (requests may  be
X     abbreviated):
X
X     _h_e_l_p [topic]
X          Send a help  message  on  all  valid  requests  or  the
X          selected topic (possibly a request) only.
X
X     _s_e_t list [option arg(s)]
X          Without the  optional  arguments,  return  the  current
X          values  for  all  options  set for _l_i_s_t; otherwise, set
X          subscriber preferences for _l_i_s_t.
X
X          _o_p_t_i_o_n can be:
X
X            _m_a_i_l: set mail preferences.
X
X              _a_r_g has to be one of the following:
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X                _a_c_k: send a copy of the current  message  to  the
X                original sender.
X
X                _n_o_a_c_k: do not send a copy of the current  message
X                to  the original sender.  This is the default for
X                newly subscribed users.
X
X                _p_o_s_t_p_o_n_e: do not send any messages to the partic-
X                ular subscriber until he changes status again.
X
X                _d_i_g_e_s_t: do not send individual  messages  to  the
X                particular subscriber. Instead, store messages in
X                a digest and send it when the  digest  exceeds  a
X                specified  number  of  lines, or when a specified
X                amount of time has passed since the  last  digest
X                was sent.
X
X                If the mail mode is changed from _d_i_g_e_s_t  to  any-
X                thing else, all messages currently stored for the
X                next digest will be sent  to  the  subscriber  at
X                once, in digest format.
X
X            _p_a_s_s_w_o_r_d: change the password (used to establish sub-
X            scriber  privileges when connecting to ListServer for
X            a "live" session and for the option below).
X
X              _a_r_g_s must be: old-password new-password.
X
X            _a_d_d_r_e_s_s: set the address the user is subscribed  with
X            (if allowed for this list).
X
X              _a_r_g_s must be: current-password new-address.
X
X            _c_o_n_c_e_a_l: set the user's visibility.
X
X              _a_r_g must be one of the following:
X
X                _y_e_s: the user is not  listed  in  _r_e_c_i_p_i_e_n_t_s  and
X                _s_t_a_t_i_s_t_i_c_s requests.
X
X                _n_o: the user's email address and  name  are  made
X                public.
X
X     _s_u_b_s_c_r_i_b_e list full_name
X          Subscribe the sender to _l_i_s_t (note, his  email  address
X          is  used  for subscription, not his _f_u_l_l__n_a_m_e). A pass-
X          word is assigned by the system  at  that  time  and  is
X          included  in  the  reply message to the new subscriber.
X          The password is to  be  used  when  connecting  to  the
X          interactive  part of the system for identification pur-
X          poses, and for changing  the  subscription  address  if
X          allowed for this list.
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X     _w_h_i_c_h
X          Get a list of local mailing lists to which  the  sender
X          has subscribed.
X
X     _u_n_s_u_b_s_c_r_i_b_e list
X          Remove the sender from the specified _l_i_s_t.
X
X     _s_i_g_n_o_f_f list
X          Alias of the unsubscribe request.
X
X     _r_e_c_i_p_i_e_n_t_s list
X          Get a list of all subscribers of _l_i_s_t. The  request  is
X          also  forwarded to all peer lists, and the servers han-
X          dling them will respond accordingly.  The user is noti-
X          fied when this request is being forwarded. If a list is
X          private, only members may issue this request.
X
X     _r_e_v_i_e_w list
X          Alias of the recipients request.
X
X     _i_n_f_o_r_m_a_t_i_o_n list
X          Get information about a particular _l_i_s_t.
X
X     _s_t_a_t_i_s_t_i_c_s list {[subscriber email address(es)] | [-all]}
X          Obtain a count of messages sent per subscriber  to  the
X          specified  _l_i_s_t, or by those subscribers given as argu-
X          ment only (wild characters are supported). If  -_a_l_l  is
X          specified,  then  statistics are compiled for all users
X          (currently subscribed or not) who have  posted  to  the
X          list in the past.  The request is also forwarded to all
X          peer lists and the servers handling them  will  respond
X          accordingly.  The user is notified when this request is
X          being forwarded.  If a list is  private,  only  members
X          may issue this request.
X
X     _r_u_n list [password cmd [args]]
X          Without the optional arguments, just list all  commands
X          that  may be executed by subscribers of this _l_i_s_t. Oth-
X          erwise, run _c_m_d with the optional _a_r_g_s, if the  correct
X          _p_a_s_s_w_o_r_d is provided. The reply will contain the output
X          from stdout and/or stderr.
X
X     _l_i_s_t_s
X          Obtain a list of mailing list addresses that  are  ser-
X          viced by this system, with a small description of their
X          purpose. If remote lists (see below) are also  defined,
X          their  addresses  and descriptive messages will also be
X          included.
X
X     _i_n_d_e_x [archive | path-to-archive] [/password] [-all]
X          Obtain an index of files in the specified  _a_r_c_h_i_v_e  (or
X          the  master  archive  if none specified) and all of its
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X          subarchives if the the -_a_l_l option is  specified.  Cer-
X          tain  archives  may  be  private,  and  these require a
X          /_p_a_s_s_w_o_r_d to be able to obtain their indices.  Archives
X          on  different places in the hierarchy may have the same
X          names and they can be distinguished by specifying paths
X          to     them;     _p_a_t_h-_t_o-_a_r_c_h_i_v_e     has    the    form
X          _a_r_c_h_i_v_e[/_a_r_c_h_i_v_e[/_a_r_c_h_i_v_e...]].  _i_n_d_e_x requests  always
X          report the paths to the archives they list.
X
X     _g_e_t <archive | path-to-archive> file [/password] [parts]
X          Get the specified _f_i_l_e from the _a_r_c_h_i_v_e given. The file
X          may have been split into smaller parts due to its size,
X          in which case each part will be  sent  in  a  different
X          email  message. If only certain _p_a_r_t_s are desired, they
X          may be given as arguments (numbers, separated by spaces
X          --  ranges are not recognized). Certain archives may be
X          private, in which case their /_p_a_s_s_w_o_r_d has to  be  pro-
X          vided  in  order to get the desired files.  Archives on
X          different places in the hierarchy  may  have  the  same
X          names and they can be distinguished by specifying paths
X          to    them;    _p_a_t_h-_t_o-_a_r_c_h_i_v_e     has     the     form
X          _a_r_c_h_i_v_e[/_a_r_c_h_i_v_e[/_a_r_c_h_i_v_e...]].   If  the  file is pure
X          binary, it will be uuencoded first.
X
X     _s_e_a_r_c_h <archive | path-to-archive> [/password] [-all]  <pat-
X          tern>
X          Search all files in the specified archive (and  all  of
X          its  subarchives  if  -_a_l_l) and return lines that match
X          the _p_a_t_t_e_r_n.  The pattern can be  an  extended  regular
X          expression with egrep(1)-style syntax, with support for
X          the following additional operators: '~', if leading the
X          regular expression it reverses its meaning; '|' and '&'
X          separate multiple regular expressions (logical  OR  and
X          AND);  '<'  '>'  group regular expressions (we preserve
X          the meaning of the parentheses from ed(1),  and  remove
X          the  meaning  of  < and > from ed(1) since in the List-
X          Server context they are either the  default,  or  inap-
X          propriate).   These  can  be used literally by escaping
X          them with '\'. In addition,  the  following  characters
X          should be defined in matched pairs: (), <>, []. _p_a_t_t_e_r_n
X          may be enclosed in single  or  double  quotes.  Pattern
X          matching is case insensitive.
X
X     _f_a_x <fax-no> <archive |  path-to-archive>  file  [/password]
X          [parts]
X          Same as the _g_e_t request except that the files are faxed
X          to the specified number.
X
X     _r_e_l_e_a_s_e
X          Get information  about  the  current  release  of  this
X          server system.
X
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X     _e_x_e_c_u_t_e password #command [arguments]
X          Intended for the system's _m_a_n_a_g_e_r,  this  will  execute
X          the  UNIX  _c_o_m_m_a_n_d with the optional _a_r_g_u_m_e_n_t_s and send
X          the output (if any) from stdout and/or  stderr  to  the
X          sender. See the sample _c_o_n_f_i_g file for _p_a_s_s_w_o_r_d defini-
X          tion.
X
X     For a list of the list administration requests that  may  be
X     issued by list owners, see the LIST OWNERS section below.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -1   Execute only once; process any requests and return con-
X          trol  to  _s_e_r_v_e_r_d(1);  any  new  messages that may have
X          arrived in the meantime will be processed  at  a  later
X          time.  Without  this option, _l_i_s_t_s_e_r_v will be listening
X          for requests for ever. _s_e_r_v_e_r_d(1) uses this  option  by
X          default when spawning _l_i_s_t_e_r_v.
X
X     -e   Echo reports to the screen; it has  no  effect  if  the
X          system has been compiled with -DSYSLOG.
X
X     -i   Go to interactive mode -- messages by _l_i_s_t_s_e_r_v are  not
X          mailed out but instead _s_e_r_v_e_r_d(1) reads them during its
X          interactive session. Do not use this option in the _c_o_n_-
X          _f_i_g file (see server(1)).
X
X     -n   By default, peer servers are notified  upon  _s_t_a_t_i_s_t_i_c_s
X          and _r_e_c_i_p_i_e_n_t_s requests. The system uses a protocol for
X          avoiding loops  as  described  in  _s_e_r_v_e_r(_1).   If  you
X          detect  loops  with  other servers, you should use this
X          option to turn off notification of peer servers.
X
X     -a LIST_ALIAS
X          Usually, subscriptions are automatic. This option turns
X          off   automatic  subscription  to  the  specified  list
X          (_L_I_S_T__A_L_I_A_S should be in capital  letters),  and  makes
X          this  list  private  (members only may issue _r_e_c_i_p_i_e_n_t_s
X          and _s_t_a_t_i_s_t_i_c_s requests). The  sender  is  notified  of
X          this  effect, and a message is sent to the list's owner
X          requesting  his/her  approval,  with  instructions  for
X          placing  the  subscription.  Notice  that the specified
X          list has to be defined beforehand via a _l_i_s_t  directive
X          in  the  _c_o_n_f_i_g  file.  This option may be repeated any
X          number of times.
X
X     -c LIST_ALIAS
X          Conceal _L_I_S_T__A_L_I_A_S from _l_i_s_t_s requests.
X
X     -r request
X          Place a restriction on the specified server _r_e_q_u_e_s_t  as
X
X
X
XAnastasios Kotsikonas                                               5
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X          outlined  above.   If the number of users on the system
X          at the time the request is about  to  be  processed  is
X          above  the  limit  given  in the _c_o_n_f_i_g file (using the
X          _r_e_s_t_r_i_c_t_i_o_n directive),  the  request  is  rejected  --
X          meant  for requests that may take a considerable amount
X          of resources such as the  _s_t_a_t_i_s_t_i_c_s  request  --  this
X          option  may  be  repeated  any  number  of  times. List
X          administration requests are not subject to  these  res-
X          trictions (see later on).
X
X     -d request
X          Disable _r_e_q_u_e_s_t, i.e. make it totally  unknown  to  the
X          server  --  this  supersedes any _d_i_s_a_b_l_e directives for
X          this request in the _c_o_n_f_i_g file, i.e. this request will
X          not  be  recognized for any list (see _s_e_r_v_e_r(_1)).  How-
X          ever, help is still available for  that  request.  List
X          administration  requests are also subject to these res-
X          trictions (see later on). This option may  be  repeated
X          any number of times.
X
X     -b request
X          All such  _r_e_q_u_e_s_ts  will  be  batch-processed  if  they
X          arrive  between the hours specified in the _c_o_n_f_i_g file.
X          This option may be repeated any number of times.
X
X     -B   Process the batch queue. This is done automatically  by
X          _s_e_r_v_e_r_d(1) after midnight every day, or when the system
X          is restarted, so no further action needs to  be  taken.
X          Caution:  do not place this option in the definition of
X          _s_e_r_v_e_r in the _c_o_n_f_i_g file. If you do so, only the batch
X          queue  will  be processed (no new requests will be pro-
X          cessed, ever).  The batch queue is processed once a day
X          only, unless the system is restarted repeatedly.
X
X     -D   Turns debugging on. When the _s_y_s_t_e_m mailmethod is used,
X          a copy of the last SMTP transaction can be found in the
X          files HOMEDIR/sent and HOMEDIR/received.
X
XPPPPEEEEEEEERRRR SSSSEEEERRRRVVVVEEEERRRRSSSS
X     The system supports the notion of remote lists. A ListServer
X     may know of remote lists served by remote servers. Yet users
X     may send requests about these remote lists to  this  server,
X     which  will  in turn forward them to the remote servers. The
X     user is notified when  a  list  is  not  local  and  his/her
X     request is about to be forwarded.  A _l_i_s_t_s request will tell
X     the user which remote lists are known to this server.
X
X     Remote lists should be made known to  this  server  only  if
X     they are served by a server of version 5.31 or higher.  This
X     restriction is posed because of incompatible requests across
X     various  list server systems; for example, the meaning of an
X     _i_n_f_o_r_m_a_t_i_o_n request is different on a BITNET  Listserv.  See
X
X
X
XAnastasios Kotsikonas                                               6
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X     the  discussion  about the _c_o_n_f_i_g file for setting up remote
X     lists.
X
X     In addition, _r_e_c_i_p_i_e_n_t_s and  _s_t_a_t_i_s_t_i_c_s  requests  are  for-
X     warded  to  the  servers handling peer lists (the -n flag to
X     _l_i_s_t_s_e_r_v turns this feature off).
X
XLLLLIIIISSSSTTTT OOOOWWWWNNNNEEEERRRRSSSS
X     List owners are individuals responsible for list administra-
X     tion  via  mail  requests. Thus, list owners may be remotely
X     located. Each list has to have  at  least  one  list  owner.
X     These owners may be different than the system's _m_a_n_a_g_e_r, and
X     have special privileges: they may issue requests  on  users'
X     behalf  (add  a user, remove a user, etc.) overriding system
X     restrictions set on regular users  (these  include  disabled
X     commands as described above), obtain reports about the lists
X     they administer, append to  the  ".aliases"  and  ".ignored"
X     files,  change  the  welcoming  (".welcome") and informative
X     (".info") messages, as well as other system  files  such  as
X     the aliases file (".aliases"), the ".ignored" file, the sub-
X     scribers file (".subscribers"), the news file (".news")  and
X     the  peers  file  (".peers"). In addition, they may moderate
X     their lists and they receive various error messages pertain-
X     ing  to their lists.  All administrative requests are author
X     authenticated and password  protected.  Whenever  a  message
X     cannot be author authenticated, the list's owner and _m_a_n_a_g_e_r
X     are notified.
X
X     On the other hand, list owners may not add restricted users;
X     this  service  can  be  provided  by contacting the system's
X     _m_a_n_a_g_e_r.
X
X     List owners may also receive copies of user requests  and/or
X     error  messages  such  as invalid postings, syntax errors on
X     requests, etc.  These options are described in the following
X     sections.
X
X     Defining list owners
X          Once a new mailing list is defined in the _c_o_n_f_i_g  file,
X          the  list's  owner address and access password are pro-
X          vided to the _l_i_s_t directive.  Of course, this  password
X          should  be  made  known to the owner; it will be needed
X          for all  administrative  requests.  Next,  the  owner's
X          address  has  to  be  registered in HOMEDIR/owners; the
X          _m_a_n_a_g_e_r simply  edits  this  file  adding  the  owner's
X          address  along  with  the  list's  name  (alias)  he is
X          assigned to, followed by any preferences  (see  below).
X          Multiple  owners  for  a  list may be defined by adding
X          their addresses to this file, and providing  them  with
X          the  list's password. However, only the primary owner's
X          address (as defined by the _l_i_s_t directive) will be used
X          as  reference  in  correspondence  and for system-error
X
X
X
XAnastasios Kotsikonas                                               7
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X          notifications, and only the primary owner will be  for-
X          warded  messages  from his moderated list for approval.
X          The _m_a_n_a_g_e_r should also add his address (login name) to
X          this file.
X
X     Owner preferences
X          The primary owner may wish  to  be  copied  on  certain
X          replies  to user requests (such as subscribe), on error
X          conditions (rejected postings, invalid requests, etc.),
X          or  on  all  cases. These preferences are listed in the
X          _o_w_n_e_r_s file on  the  line  his  address  and  list  are
X          defined. Valid preferences are:
X
X            CCSET: copy on _s_e_t requests.
X
X            CCSUBSCRIBE: copy on _s_u_b_s_c_r_i_b_e requests.
X
X            CCUNSUBSCRIBE: copy on _u_n_s_u_b_s_c_r_i_b_e requests.
X
X            CCRECIPIENTS: copy on _r_e_c_i_p_i_e_n_t_s requests.
X
X            CCINFORMATION: copy on _i_n_f_o_r_m_a_t_i_o_n requests.
X
X            CCSTATISTICS: copy on _s_t_a_t_i_s_t_i_c_s requests.
X
X            CCRUN: copy on _r_u_n requests.
X
X            CCPRIVATE: copy on requests rejected because they are
X            open only to the list's members.
X
X            CCERRORS: copy on various error conditions.
X
X            CCALL: all of the above.
X
X          Owner preferences are optional.
X
X     The _m_a_n_a_g_e_r may define preferences for himself  as  well  by
X     using  the keyword _s_e_r_v_e_r in place of a list alias. However,
X     such preferences make sense only in the following cases:
X
X       CCGET: copy on _g_e_t requests.
X
X       CCINDEX: copy on _i_n_d_e_x requests.
X
X       CCLISTS: copy on _l_i_s_t_s requests.
X
X       CCRELEASE: copy on _r_e_l_e_a_s_e requests.
X
X       CCHELP: copy on _h_e_l_p requests.
X
X       CCERRORS: copy on various error conditions.
X
X
X
X
XAnastasios Kotsikonas                                               8
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X       CCALL: all of the above.
X
X     Manager preferences are optional.
X
X     Owner privileges
X          Whenever an owner issues a user  request  on  a  user's
X          behalf  (see  below),  all restrictions, including dis-
X          abled commands, do not apply. All other  administrative
X          requests   are  subject  to  restrictions  set  by  the
X          _m_a_n_a_g_e_r.  All requests (user  and  administrative)  are
X          subject to batch-processing.
X
X     Administrative requests
X          The following requests may be issued by a list's owner:
X
X     _s_y_s_t_e_m list password user-address #user-request
X          This request overrides all system restrictions and exe-
X          cutes  _u_s_e_r-_r_e_q_u_e_s_t  on  behalf  of  _u_s_e_r-_a_d_d_r_e_s_s; this
X          address has to appear as listed in  the  ".subscribers"
X          file,  where  applicable.  The most frequent use of the
X          _s_y_s_t_e_m request is to subscribe  a  user  to  a  private
X          list. For example:
X
X            system herc herc1 john@foo #subscribe herc Some Name
X
X          If a _u_s_e_r-_r_e_q_u_e_s_t refers to a list, this list has to be
X          _l_i_s_t,  so  that  a list's owner may not have privileges
X          over another list's  affairs.  Note  that  all  replies
X          about  _u_s_e_r-_r_e_q_u_e_s_t  are forwarded to _u_s_e_r-_a_d_d_r_e_s_s, not
X          the owner; therefore, care has to  be  taken  to  avoid
X          syntax  errors.  The  _s_y_s_t_e_m  request is not subject to
X          restrictions, disabled requests, and private list  sub-
X          scription  verification (it is still subject to private
X          list review as outlined above, and batching). To remove
X          a member from his list, the owner may issue the follow-
X          ing request:
X
X            system herc herc1 john@foo #unsubscribe herc
X
X          To bypass restrictions and review his list,  the  owner
X          may issue the following:
X
X            system venus venus1 his-address #review venus
X
X          In general, _u_s_e_r-_r_e_q_u_e_s_t may be any of  the  recognized
X          user  requests described under _l_i_s_t_s_e_r_v. The pound sign
X          is mandatory.  There is no help available to users  for
X          this request for security reasons.
X
X     _a_p_p_r_o_v_e list password tag
X          Whenever a new message arrives for a moderated  list  a
X          copy  is  sent  to  the  list's  owner  soliciting  his
X
X
X
XAnastasios Kotsikonas                                               9
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X          approval -- proper instructions for approving  or  dis-
X          carding  a  message are included. This request approves
X          the message identified by the _t_a_g number for posting to
X          _l_i_s_t. The tag number is provided to the list's owner by
X          _l_i_s_t_s_e_r_v and is unique.
X
X     _d_i_s_c_a_r_d list password tag
X          In contrast to the above  request,  this  discards  the
X          message  identified  by  _t_a_g.  Messages  that  are  not
X          approved or discarded remain in  the  list's  _m_o_d_e_r_a_t_e_d
X          file (see _l_i_s_t(_1)).
X
X     _r_e_p_o_r_t_s list password
X          Obtain all reports pertinent to _l_i_s_t;  this  will  send
X          two   mail  messages:   one  with  the  current  report
X          (HOMEDIR/lists/ALIAS/.report.list), and  one  with  the
X          previously                 archived                ones
X          (HOMEDIR/lists/ALIAS/.rep.list.acc);  see  the  REPORTS
X          section  below.  Once the ".rep.list.acc" file is sent,
X          it is shrunk in size, therefore the owner  should  make
X          sure he keeps the copy he receives.
X
X          This request has no effect if the system is using  sys-
X          log(3) to generate reports.
X
X     _e_d_i_t list password file
X          Obtain the specified _f_i_l_e for editing; candidate  files
X          are:
X
X            _a_l_i_a_s_e_s: obtain the list's aliases file.
X
X            _i_g_n_o_r_e_d:  obtain  the  list's   list   of   unwelcome
X            addresses.
X
X            _i_n_f_o: obtain the list's informative message.
X
X            _s_u_b_s_c_r_i_b_e_r_s: obtain the list's subscribers list.
X
X            _w_e_l_c_o_m_e: obtain the list's welcoming message.
X
X            _n_e_w_s: obtain the list's  list  of  newsgroup  connec-
X            tions.
X
X            _p_e_e_r_s: obtain the list's peers.
X
X     _p_u_t list password keyword [args]
X          This  enables  the  list's  owner  to  append  to   the
X          ".aliases" and ".ignored" files, and replace his list's
X          ".welcome",  ".info",  ".aliases",  ".ignored",  ".sub-
X          scribers", ".news" and ".peers" files, depending on the
X          _k_e_y_w_o_r_d. Valid _k_e_y_w_o_r_ds are:
X
X
X
X
XAnastasios Kotsikonas                                              10
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X            _a_l_i_a_s: Add a user address alias  to  the  list's  and
X            system's  ".aliases" files (see .ALIASES below). This
X            requires the new address and  the  address  used  for
X            subscription as arguments:
X
X            put <list> <password> alias <new-alias>  <address-as-
X            subscribed>
X
X            For example:
X
X              put venus venus1 alias foo!john john@foo
X
X            _i_g_n_o_r_e: Add a user address to  the  list's  ".ignore"
X            file  only. Of course this address has to be provided
X            as argument:
X
X            put <list> <password> ignore  <address-as-subscribed-
X            or-aliased>
X
X            For example:
X
X              put ermis ermis1 ignore jack@foo
X              put ermis ermis1 ignore foo!jack
X
X            _w_e_l_c_o_m_e,
X            _i_n_f_o,
X            _a_l_i_a_s_e_s,
X            _i_g_n_o_r_e_d,
X            _s_u_b_s_c_r_i_b_e_r_s,
X            _n_e_w_s,
X            _p_e_e_r_s: Create a new system file.
X
X            For example:
X
X              put <list> <password> subscribers
X              tasos ACK PASSWORD NO Tasos Kotsikonas
X              john NOACK PASS1 NO John Doe
X
X            No arguments are needed. The text that is  to  go  to
X            the  corresponding  file starts at the line following
X            this request and spans till the end of the mail  mes-
X            sage.  Thus, no more requests can be made in the same
X            mail message -- they are  treated  as  regular  text;
X            signature  lines  also  signify the end of text, pro-
X            vided they start with "--" in a single line.
X
X          A confirmation is sent to the owner once a _p_u_t  request
X          is successfully processed.
X
XOOOOWWWWNNNNEEEERRRRSSSS
X     The format of the _o_w_n_e_r_s file is as follows:
X
X
X
X
XAnastasios Kotsikonas                                              11
X
X
X
X
X
X
Xlistserv(1)              USER COMMANDS                listserv(1)
X
X
X
X     One entry per line; each entry is the full email address  of
X     a list's owner, followed by the list alias he owns, followed
X     by any optional preferences. If the keyword _s_e_r_v_e_r is speci-
X     fied  in  place  of  the  list  alias,  then preferences are
X     defined for the _m_a_n_a_g_e_r.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     catmail(1), farch(1),  list(1),  queue(1),  server(1),  ser-
X     verd(1), start(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                              12
X
X
X
*-*-END-of-doc/listserv.nr-*-*
echo x - doc/queue.nr
sed 's/^X//' >doc/queue.nr <<'*-*-END-of-doc/queue.nr-*-*'
X
X
X
Xqueue(1)                 USER COMMANDS                   queue(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     qqqquuuueeeeuuuueeeedddd - UNIX ListServer mail queue daemon
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     qqqquuuueeeeuuuueeeedddd <ffffrrrreeeeqqqquuuueeeennnnccccyyyy>
X
XNNNNAAAAMMMMEEEE
X     ppppqqqquuuueeeeuuuueeee - process the specified mail queue files
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     ppppqqqquuuueeeeuuuueeee [----eeee] [----DDDD] <ffffiiiilllleeeessss>
X
XOOOOVVVVEEEERRRRVVVVIIIIEEEEWWWW
X     This part of the UNIX ListServer system can be employed only
X     when  using the _s_y_s_t_e_m mailmethod (see _s_e_r_v_e_r(1)).  Messages
X     and replies to requests not delivered due to  network  prob-
X     lems   are   queued   by   the   system   in  the  directory
X     HOMEDIR/mqueue. This part of the  system  attempts  periodic
X     redelivery  of these files. If problems still persist, files
X     (messages) that cannot be delivered are requeued.
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN:::: qqqquuuueeeeuuuueeeedddd
X     This is the daemon that looks for files in the  mail  queue.
X     It  uses  _p_q_u_e_u_e  for redelivery. The queue is checked every
X     _f_r_e_q_u_e_n_c_y seconds. Whenever an  error  occurs  with  _p_q_u_e_u_e,
X     _q_u_e_u_e_d  sends  a  mail message to _m_a_n_a_g_e_r (as defined in the
X     _c_o_n_f_i_g file) and aborts.
X
X     _q_u_e_u_e_d is not spawned by _s_t_a_r_t(1) in  order  to  reduce  the
X     number  of  processes running, since the probability of mes-
X     sages being queued  is  very  low.  Instead,  it  should  be
X     started  manually whenever there are files in the mail queue
X     directory.
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN:::: ppppqqqquuuueeeeuuuueeee
X     The _f_i_l_e_s given as arguments  are  redelivered.  If  any  of
X     these  cannot  be redelivered, they are requeued and will be
X     processed in the  next  run.  _p_q_u_e_u_e  reports  to  the  file
X     HOMEDIR/.report.pqueue
X
X     The following command line options are recognized:
X
X     -e   Echo reports to the screen; it has  no  effect  if  the
X          system has been compiled with -DSYSLOG.
X
X     -D   Turns debugging on; a copy of the last SMTP transaction
X          can   be   found   in   the   files   HOMEDIR/sent  and
X          HOMEDIR/received. Warning: these files are also used by
X          _l_i_s_t(1) and _l_i_s_t_s_e_r_v(1) when they have their debug mode
X          on, so use caution.
X
X
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xqueue(1)                 USER COMMANDS                   queue(1)
X
X
X
XNNNNOOOOTTTTEEEE
X     This mail queueing system should not be  confused  with  the
X     one implemented by sendmail(1).
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     server(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
*-*-END-of-doc/queue.nr-*-*
echo x - doc/server.nr
sed 's/^X//' >doc/server.nr <<'*-*-END-of-doc/server.nr-*-*'
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     UNIX ListServer version 6.0
X
XOOOOVVVVEEEERRRRVVVVIIIIEEEEWWWW
X     This is a system that implements various mailing lists  with
X     one  list  server. It is automated, and obliterates the need
X     for user intervention and maintenance of multiple aliases of
X     the  form  "list,  list-owner,  list-request", etc. There is
X     support  provided  for  public  and   private   hierarchical
X     archives,  moderated  and  non-moderated  lists, peer lists,
X     peer servers, private lists, address aliasing, news  connec-
X     tions  and gateways, mail queueing, digests, list ownership,
X     owner preferences, crash recovery, batch processing,  confi-
X     gurable headers, regular expressions, archive searching, and
X     live user connections via TCP/IP.
X
XOOOOBBBBTTTTAAAAIIIINNNNIIIINNNNGGGG IIIINNNNFFFFOOOORRRRMMMMAAAATTTTIIIIOOOONNNN
X     mailing lists, aliases, news groups, peer lists
X          See list(1).
X
X     user requests, list owners, peer servers, remote lists
X          See listserv(1).
X
X     distributing incoming mail
X          See catmail(1).
X
X     archiving files
X          See farch(1).
X
X     archiving of lists' messages
X          See below.
X
X     processing the mail queue
X          See queue(1).
X
X     options for the system's daemon
X          See serverd(1).
X
X     starting and stopping the system
X          See start(1).
X
X     live connections
X          See iul(1) and serverd(1).
X
XSSSSYYYYSSSSTTTTEEEEMMMM SSSSEEEETTTTUUUUPPPP
X     The UNIX ListServer system is installed  under  the  HOMEDIR
X     directory  as defined in src/Makefile; HOMEDIR points to the
X     top level directory  of  the  installation;  a  _s_e_r_v_e_r  user
X     account  has to be setup. The system should be installed and
X     set up using this account, and always be started  from  this
X     account  as  well (see _s_t_a_r_t(1)).  Then, the following alias
X     has to be  defined  in  /etc/aliases,  /usr/lib/aliases   or
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     /usr/ucblib/aliases  (depending  on  your  system) for list-
X     server (where users send their requests):
X
X       listserver: "|HOMEDIR/catmail -r -f"
X
X     Keep in mind, that the recommended alias before version  6.0
X     was  "listserv", so you may wish to use that for compatibil-
X     ity purposes.
X
X     Various mailing lists are set up in a similar  fashion;  see
X     _l_i_s_t(1).
X
X     It is important to keep in mind that mailers should  convert
X     any  text  lines  (not  header  lines)  of incoming messages
X     starting with "From ", to ">From ".  This  is  automatically
X     done by the -f flag to _c_a_t_m_a_i_l(1).
X
X     The _c_a_t_m_a_i_l(1) utility is used to append  incoming  mail  to
X     the  appropriate  files.  When  properly  set up, it has the
X     setuid bit turned on. Since execute permission is granted to
X     everybody,  it  is  possible  for  anyone to append to these
X     files; however, each time _c_a_t_m_a_i_l(1) is run, it reports  the
X     user id and user name whom access was granted to.
X
X     If the system is to go  interactive  (see  _s_e_r_v_e_r_d(1)),  you
X     have to add the following line in your /etc/services file:
X
X                         ulistserv   372/tcp
X
X     See also src/Makefile.
X
X     How the system operates is defined in the _c_o_n_f_i_g  file  (see
X     below).  Once the system is loaded, you will have to run the
X     script _s_e_t_u_p; this will ensure that all necessary files  and
X     directories  are  present,  and  have the right permissions.
X     Before starting  the  system,  you  may  wish  to  edit  the
X     ".ignored"  file  in  HOMEDIR (see _l_i_s_t(1)).  At this point,
X     you may also wish to alter the help files  in  HOMEDIR/help;
X     each file in that directory corresponds to one of the recog-
X     nized requests.  Help files may be  simple  text  files,  or
X     shell  scripts  starting  with "#!"  in the first line, fol-
X     lowed by the shell to execute.
X
X     File protections are a major issue and the requirements  may
X     vary  from system to system. The problems usually arise from
X     inadequate write permissions while  appending  mail  to  the
X     various mail files. The system uses the environment variable
X     ULISTSERVER_UMASK in the same way as umask(1) is  used  when
X     updating  files.  If not set, the default is 066. A value of
X     026 should be acceptable in most cases. This variable should
X     be set in both .cshrc and .profile.
X
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     Finally, experiment with the various mail methods  described
X     below,  until  you get the proper "From " line in the begin-
X     ning of the outgoing message; for instance, when _l_i_s_t_s_e_r_v(1)
X     is  replying  to  a  request,  you should see something like
X     "From listserver@your-domain"  as  the  first  line  of  the
X     header;  or when the list abc is distributing messages, this
X     line should look like "From abc@your-domain". If you  cannot
X     get   it   to  work,  i.e.  you  get  something  like  "From
X     server@your-domain" then the server system may have  to  run
X     with  superuser  privileges. In this case, the server userid
X     should be the same as root's. Also consult the PORT SPECIFIC
X     section  below, for suggested mailmethods. You will not have
X     any of these problems if you use the _s_y_s_t_e_m mailmethod  (see
X     below).
X
X     If this part of the setup is not done properly,  peer  lists
X     will  not  work at all and users may not be able to reply to
X     the list. As a final note, if _t_e_l_n_e_t does  not  seem  to  be
X     sending any mail, it is a bug with the telnet implementation
X     on your system: telnet cannot have its input  redirected  or
X     piped, and should be reported to your vendor.
X
X     The last step for a complete set up is to define your system
X     in the _c_o_n_f_i_g file.
X
XCCCCOOOONNNNFFFFIIIIGGGG
X     The server system is defined in the _c_o_n_f_i_g file; an  example
X     file  is  provided  with  the  system.  This file is used by
X     _s_t_a_r_t(1), _s_e_r_v_e_r_d(1), _l_i_s_t(1) and _l_i_s_t_s_e_r_v(1), and is not  a
X     shell script.
X
X     The following keywords (directives) are  recognized  and  is
X     advisable  that  each one of them starts at column 1; direc-
X     tives may span multiple lines if each line is terminated  by
X     &\n:
X
X     _o_r_g_a_n_i_z_a_t_i_o_n name
X          This defines your organization when posting to news and
X          upon  connection  establishment of a live session. _n_a_m_e
X          may include blanks.
X
X     _s_e_r_v_e_r _l_i_s_t_s_e_r_v_e_r@your-domain command-line-options
X          This defines the list server; the first argument is the
X          full    email    address    of    the    server   (e.g.
X          listserver@foo.edu)  followed  by  any   command   line
X          options  to  be  used when _s_e_r_v_e_r_d spawns _l_i_s_t_s_e_r_v (see
X          _s_e_r_v_e_r_d(1), _l_i_s_t_s_e_r_v(1)).
X
X     _l_i_s_t list_alias email-addr owner-addr password cmd-line-opt
X          This defines a mailing list; the first argument is  the
X          list's  alias  in  /etc/aliases,  /usr/lib/aliases   or
X          /usr/ucblib/aliases (see _l_i_s_t(1)), followed by its full
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          email  address,  followed  by  its  owner's  full email
X          address, followed by the list's  password  for  mainte-
X          nance,  followed by any command line options to be used
X          when _s_e_r_v_e_r_d(1) spawns _l_i_s_t(1).
X
X     _h_e_a_d_e_r list { [Header-Line:] ... }
X          It specifies precious header lines  from  the  original
X          sender's  message  that are to be preserved during dis-
X          tribution. The _l_i_s_t has to be defined before, and  pre-
X          cious header lines are specified within the enclosing {
X          and }, possibly spanning multiple lines.
X
X     _d_e_f_a_u_l_t list { [option = value] ... }
X          Determine various default values for _l_i_s_t for  new  and
X          current subscribers; the list has to be defined before.
X          Pairs of options and values may be repeated any  number
X          of  times,  and  may  span  multiple  lines; if certain
X          options are missing, then  system  defaults  are  used.
X          Valid _o_p_t_i_o_ns are:
X
X            _a_d_d_r_e_s_s: turn user-settable subscription addresses on
X            or off. Valid values are:
X
X              _f_i_x_e_d: users are not allowed to change the  address
X              they are subscribed with via the _s_e_t <_l_i_s_t> _a_d_d_r_e_s_s
X              request (see  _l_i_s_t_s_e_r_v(1)).   This  is  the  system
X              default as well.
X
X              _v_a_r_i_a_b_l_e: opposite of the above.
X
X            _m_a_i_l: select the  default  mail  mode  for  new  sub-
X            scribers. Valid values are:
X
X              _a_c_k: see _s_e_t in _l_i_s_t_s_e_r_v(1).
X
X              _n_o_a_c_k: see _s_e_t in _l_i_s_t_s_e_r_v(1); this is  the  system
X              default.
X
X              _p_o_s_t_p_o_n_e: see _s_e_t in _l_i_s_t_s_e_r_v(1) (a rather  useless
X              setting indeed).
X
X              _d_i_g_e_s_t: see _s_e_t in _l_i_s_t_s_e_r_v(1).
X
X            _p_a_s_s_w_o_r_d: define a single password for new users;  if
X            missing,  the  system  assigns a random password. Any
X            string that contains no white spaces may be used.
X
X            _c_o_n_c_e_a_l: determine  the  default  visibility  of  new
X            users; see _s_e_t in _l_i_s_t_s_e_r_v(1). Valid options are:
X
X              _y_e_s:  hidden   from   _r_e_c_i_p_i_e_n_t_s   and   _s_t_a_t_i_s_t_i_c_s
X              requests.
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X              _n_o: this is the system default.
X
X     _c_e_i_l_i_n_g list max_messages
X          Restrict the number of  messages  processed  daily  for
X          _l_i_s_t to _m_a_x__m_e_s_s_a_g_e_s; this includes posted and rejected
X          messages.
X
X     _r_e_m_o_t_e alias email-addr listserver-addr [host [port]]  #Com-
X          ment
X          This makes a remote list known to the server. A  remote
X          list  is another mailing list served by another server,
X          and your server is now capable  of  forwarding  to  the
X          proper  remote list server any requests sent about this
X          remote list to your server. _a_l_i_a_s is the name by  which
X          the  remote  list  is  known by, _e_m_a_i_l-_a_d_d_r is the full
X          email address of the remote  list,  _l_i_s_t_s_e_r_v_e_r-_a_d_d_r  is
X          the  full  email address of the remote list server, and
X          #_C_o_m_m_e_n_t will be used upon a _l_i_s_t_s request made to this
X          server  (the  pound  sign is mandatory). _h_o_s_t is either
X          the DNS name or IP address of the remote  server,  used
X          to connect to that server when a live request refers to
X          that list; the _p_o_r_t should  be  specified  if  not  the
X          default (372) (see _s_e_r_v_e_r_d(1)).
X
X          Remote lists may have the same _a_l_i_a_s  and  they  should
X          not  be  confused with peer lists. However, each remote
X          list should be defined only once.  See  the  discussion
X          about  PEER SERVERS  (_l_i_s_t_s_e_r_v(1)) for further informa-
X          tion.
X
X     _f_a_x fax-program options
X          Define the fax program to use for _f_a_x requests (if any)
X          and  any  command line options the program may use. The
X          program should also be capable of accepting the file to
X          fax  from  its  standard input; otherwise a script will
X          have to be used instead which will in turn use the  fax
X          program  appropriately.  If  this  directive is left or
X          commented out,  then  _f_a_x  requests  are  automatically
X          turned of.
X
X          The actual faxing program is called as follows:
X
X                   _f_a_x-_p_r_o_g_r_a_m _o_p_t_i_o_n_s fax-number < file
X
X          This request may not be issued  from  a  live  session,
X          since  fax  programs work on email messages and extract
X          the sender's address for  reference  when  sending  the
X          cover page of the fax.
X
X     _u_n_i_x__c_m_d  list_alias  password  name  'unix-command  [args]'
X          #Comment
X          Allow a _l_i_s_t__a_l_i_a_s's subscribers only  to  execute  the
X
X
X
XAnastasios Kotsikonas                                               5
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          above  _u_n_i_x-_c_o_m_m_a_n_d  aliased  to  _n_a_m_e. The _p_a_s_s_w_o_r_d is
X          made known to users who are to be given this privilege.
X          The  oprional _a_r_g_s may be literal arguments to the com-
X          mand as well as the special sequences $_n, where _n is  a
X          digit  from  1 to 9, or *; their meaning is that of the
X          Bourne shell, and  they  will  be  substituted  by  the
X          user's actual arguments. For example:
X
X          unix_cmd ermis pass1 swap '/bin/echo $2 $1' #Syntax: swap <arg1> <arg2>
X
X          will swap the user's first and  second  arguments.  The
X          user would submit a request that could look like this:
X
X                      run ermis pass1 swap arg1 arg2
X
X          No security checks are made except to ensure  that  the
X          arguments  contain  no `, |, :, < and >, so the _m_a_n_a_g_e_r
X          is encouraged to review and verify the proper function-
X          ing  of  _u_n_i_x-_c_o_m_m_a_n_d.  The  _C_o_m_m_e_n_t is used when users
X          issue 'run <list>' query requests.
X
X     _s_e_r_v_e_r_d command-line-options
X          This defines the command line options that _s_t_a_r_t(1)  is
X          to use when spawning _s_e_r_v_e_r_d(1).
X
X     _r_e_s_t_r_i_c_t_i_o_n nusers
X          If a restriction is placed on a ListServer request (via
X          a  -r  command line option to _l_i_s_t_s_e_r_v(1)), this direc-
X          tive defines the threshold number of users, above which
X          the restriction will take effect.
X
X     _d_i_s_a_b_l_e list_alias request
X          The specified server _r_e_q_u_e_s_t is disabled for the speci-
X          fied  list,  and  all  such  requests for this list are
X          rejected, except when issued by privileged users  (list
X          owners).  The  _l_i_s_t__a_l_i_a_s has to be defined (via a _l_i_s_t
X          directive) before any requests can be disabled.
X
X     _m_a_n_a_g_e_r full-email-address
X          This defines the recipient of  all  system  error  mes-
X          sages,  and  the  system's administrator and caretaker;
X          _f_u_l_l-_e_m_a_i_l-_a_d_d_r_e_s_s can be any valid user name that  can
X          be  reached  via  email by your system.  The _m_a_n_a_g_e_r is
X          usually the person responsible for the server account.
X
X     _c_o_m_m_e_n_t _s_e_r_v_e_r #Actual comment
X
X     _c_o_m_m_e_n_t list_alias #Actual comment
X          A 'X-Comment:'  line  is  included  in  every  outgoing
X          list/server  message,  and the text following the pound
X          sign will be copied every time;  note  that  the  pound
X          sign  is  mandatory  for the definition but will not be
X
X
X
XAnastasios Kotsikonas                                               6
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          part of the actual string.  A list's  comment  is  also
X          used  for  the  _l_i_s_t_s request to specify the purpose of
X          the particular mailing list. No  'X-Comment:'  line  is
X          included  if this directive is not defined for the par-
X          ticular list and/or server.
X
X     _d_i_g_e_s_t list_alias lines hours
X          If any subscriber to the list requests digests, a  dig-
X          est will be sent when its length exceeds _l_i_n_e_s, or when
X          _h_o_u_r_s have passed without a digest being sent.  Digests
X          for  a  list  are collected only if at least one of its
X          subscribers has set his mail mode to _d_i_g_e_s_t.
X
X     _a_r_c_h_i_v_e list_alias dir filename [archive]  [password]  [dig-
X          est]
X          Request that  all  public  messages  are  automatically
X          archived;  _l_i_s_t__a_l_i_a_s  is  the  list's name, _d_i_r is the
X          directory that the archived files will  be  located  (a
X          full path has to be specified, and the directory has to
X          be  writable  by  the  server  uid),  _a_r_c_h_i_v_e  is   the
X          archive's name and is a relative path under the default
X          archive (listserver) or "-" for the  default,  _p_a_s_s_w_o_r_d
X          is an optional password ("-" if none) to be used if the
X          archive is meant to be private, and the  optional  key-
X          word  _d_i_g_e_s_t  will  force  only digests to be archived;
X          _f_i_l_e_n_a_m_e specifies the format of  the  archived  files,
X          and  may contain any lower case characters and numbers,
X          as well as the following special sequences:
X
X            %m: Numeric month (01 - 12)
X
X            %d: Numeric day of the month (01 - 31)
X
X            %y: Year (00 - 99)
X
X            %j: Julian date (001 - 366)
X
X            %h: Month name (Jan - Dec)
X
X            %a: Contents of the  Archive-Name:  header  line,  if
X            present;  cannot  be  used  if archiving digests. The
X            Archive-Name: header line has to be manually inserted
X            by  the  sender of the message; it may show up in the
X            body of the message as well.
X
X            %#: Digest number; can only be used if archiving dig-
X            ests.
X
X            %1: First word of the first  non-blank  line  of  the
X            message.
X
X            %v: Volume number, if the message contains a  'Volume
X
X
X
XAnastasios Kotsikonas                                               7
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X            # Number #' line before the actual message.
X
X            %n: Issue number, if the message contains a 'Volume #
X            Number #' line before the actual message.
X
X            %%: The character %
X
X          For example, to archive messages daily:
X
X          archive ermis HOMEDIR/archives/lists/ermis %y%m%d listserver/lists/ermis
X
X          or to archive only files sent to the list:
X
X          archive venus /ftp/lists/venus vol-%v.num-%n listserver/lists/venus johny-be-good
X
X          and in this case the sender that wants  a  file  to  be
X          archived would precede it with a line similar to:
X
X          Volume 10 Number 5
X
X          followed by the actual file.
X
X          When a new archive file is created, the  Subject:  line
X          is  used  as  the  short  descriptive  message  for the
X          archive.
X
X     _f_r_e_q_u_e_n_c_y seconds
X          How often should _s_e_r_v_e_r_d(1) check for  new  mail.  When
X          _s_e_c_o_n_d_s is set to zero, _s_e_r_v_e_r_d does not sleep.
X
X     _b_a_t_c_h start stop
X          Specify the hours (in military time) when requests  are
X          to  be  batched.   The defaults are 8 and 20. _s_t_a_r_t and
X          _s_t_o_p should be within the same day (e.g. 8 am and 1  am
X          the following day are not valid).
X
X     _l_i_m_i_t keyword arguments
X          Used to set certain limits in the system;  _k_e_y_w_o_r_d  can
X          be:
X
X            _m_e_s_s_a_g_e: limit the size of distributed messages to  a
X            certain  number  of bytes, which is the _a_r_g_u_m_e_n_t fol-
X            lowing this keyword.  A notification is sent back  to
X            the sender when this limit is exceeded, including the
X            first few  lines  of  his/her  original  message  for
X            reference.
X
X            _f_i_l_e_s: limit the size of archive files  that  can  be
X            sent  out  to a certain number of bytes, which is the
X            _a_r_g_u_m_e_n_t following this keyword.  When this limit  is
X            reached,  the  file  is automatically split into sub-
X            parts.
X
X
X
XAnastasios Kotsikonas                                               8
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     _p_r_e_c_e_d_e_n_c_e string
X          The header of each outgoing  message  contains  a  Pre-
X          cedence: line; _s_t_r_i_n_g can be:
X
X            bulk, junk, first-class,  none:  primarily  used  for
X            vacation programs.
X
X     _o_p_t_i_o_n keyword
X          This defines a series of system-dependent options; _k_e_y_-
X          _w_o_r_d can be:
X
X            _s_y_s_v__p_s: the system will assume a System V version of
X            ps(1).
X
X            _b_s_d__p_s: the system  will  assume  a  BSD  version  of
X            ps(1).
X
X            _b_s_d__m_a_i_l: define it if BSD (UCB) mail is available on
X            your  system;  if  this  is  the case, make sure that
X            /usr/ucb/mail is the path (or a link) to it; also see
X            src/Makefile.   This is used to notify the _m_a_n_a_g_e_r of
X            any error conditions.
X
X            _b_a_d__t_e_l_n_e_t: if the mail method used  (see  below)  is
X            _t_e_l_n_e_t and the system seems to send out only one mes-
X            sage and then go to sleep, use this option.
X
X            _p_o_s_t__m_a_i_l: this will force the system  to  post  mes-
X            sages to news groups (using _i_n_e_w_s), using the groups'
X            names (e.g. misc.test). Make sure that _i_n_e_w_s  resides
X            in /usr/lib/news, or /usr/lib/news/inews is a link to
X            it (also see src/Makefile).
X
X            _g_a_t_e__m_a_i_l: this will force the system  to  gate  mes-
X            sages to news, using the gateways' email addresses.
X
X            _i_g_n_o_r_e__i_n_v_a_l_i_d__r_e_q_u_e_s_t_s: ignore all unrecognized user
X            requests and process valid ones only; by default, the
X            system aborts  processing  requests  upon  the  first
X            invalid  one,  in  which  case a reply is sent to the
X            user and all subsequent requests are flushed.
X
X            _r_e_l_a_x_e_d__s_y_n_t_a_x: by default, the system complains when
X            a request is given more arguments than expected. This
X            turns such checking off.
X
X     _m_a_i_l_m_e_t_h_o_d method [arguments]
X          Every outgoing message should begin with a line of  the
X          form:
X
X            From listserver@your-domain
X
X
X
X
XAnastasios Kotsikonas                                               9
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X            or
X
X            From list_alias@your-domain
X
X          which depends on the mail _m_e_t_h_o_d used:
X
X            _s_y_s_t_e_m: the recommended method; it provides a unified
X            approach  to  sending mail and it has been ported and
X            tested  on  lots  of   systems   (see   the   section
X            PORT SPECIFIC  below).  In addition, since a full set
X            of the SMTP protocol is implemented, _m_a_n_a_g_e_r and list
X            owners  will  be  notified  of  invalid addresses and
X            various system  problems.   Moreover,  mail  will  be
X            queued  when it cannot be delivered, in the directory
X            HOMEDIR/mqueue; see _q_u_e_u_e(_1) for more information  on
X            how to process the mail queue.
X
X            This method requires host TCP/IP  and  internet  sup-
X            port;  consult  the src/README file for more informa-
X            tion.
X
X            _t_e_l_n_e_t: to be used only when _s_y_s_t_e_m is  inappropriate
X            and telnet(1) is available on your host.
X
X            _e_n_v__v_a_r: usually followed by _L_O_G_N_A_M_E  /_b_i_n/_r_m_a_i_l,  or
X            _L_O_G_N_A_M_E  /_u_s_r/_l_i_b/_s_e_n_d_m_a_i_l  -_b_a (-ba is mandatory) --
X            to be used only when the previous methods  are  inap-
X            propriate.
X
XMMMMAAAAIIIILLLL LLLLOOOOOOOOPPPPSSSS
X     The system uses the following  protocol  for  avoiding  mail
X     loops  between a list and news connections: In the header of
X     the outgoing message an "Originator: " field is  added.  For
X     each  list  plus  listserver, a log of the most recent (500)
X     Message-Id's is kept.  Whenever a message is  received,  the
X     Originator, Reply-To and Message-Id fields are extracted and
X     looked up in the ".ignored" and ".message.ids" files in  the
X     list's  subdirectory.  Gateways  that  feed back to the list
X     should preserve at least the Reply-To and Message-Id fields.
X     The  Originator  and Message-Id fields are preserved by this
X     system. A new Reply-To is tacked on when redistributing mail
X     locally from a peer or a news feed.
X
X     To avoid mail loops when forwarding ListServer  requests  to
X     peers,  the  system  looks  up  the  Message-Id field in the
X     ".message.ids" file  in  HOMEDIR.   If  this  field  is  not
X     preserved  by  the peer ListServer, it is suggested that you
X     turn off  request  forwarding  when  connecting  with  peers
X     served  by  such systems, until a unified approach is taken.
X     The Message-Id field is preserved by this  ListServer.  This
X     system  is  also  using  a  special  Subject  field, totally
X     proprietary and non-standard, in order to avoid  unnecessary
X
X
X
XAnastasios Kotsikonas                                              10
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     forwarding of requests, thus cutting down on email traffic.
X
XCCCCRRRRAAAASSSSHHHH RRRREEEECCCCOOOOVVVVEEEERRRRYYYY
X     It is possible that a message delivery was interrupted by  a
X     user (_s_t_a_r_t -_k), or by the system (crash/reboot). A built-in
X     mechanism guarrantees to pick up delivery again  from  where
X     it left off, if the system is restarted using _s_t_a_r_t(1). Both
X     _s_t_a_r_t(1) and _l_i_s_t(1) report to the effect that mail delivery
X     was interrupted and will resume. On the other hand, process-
X     ing of interrupted requests will not resume and  all  unpro-
X     cessed requests will be lost.
X
XCCCCOOOOMMMMMMMMUUUUNNNNIIIICCCCAAAATTTTIIIINNNNGGGG
X     Users send requests  to  _l_i_s_t_s_e_r_v_e_r@_y_o_u_r-_d_o_m_a_i_n  and  public
X     messages to the various _l_i_s_t__a_l_i_a_s@_y_o_u_r-_d_o_m_a_i_n.
X
XAAAARRRRCCCCHHHHIIIIVVVVEEEESSSS
X     The server has an archiving capability  of  files.  Archives
X     may  be  public  so  that anyone can get files from them, or
X     private so that only privileged users may obtain files  from
X     them.      There      is     a     master     archive     in
X     HOMEDIR/archives/listserver  where   all   subarchives   are
X     defined. The archives keep lists of files that are available
X     to users via a _g_e_t request, and index  of  subarchives  that
X     are available to users via an _i_n_d_e_x request.
X
X     As outlined in _l_i_s_t(1),  each  list's  public  messages  are
X     automatically  saved  under  the  list's subdirectory in the
X     file _a_r_c_h_i_v_e.  This file may periodically be placed  in  the
X     system's  archives  by  hand (if not archiving automatically
X     via the _a_r_c_h_i_v_e directive), or may point to  /dev/null.  The
X     archived  files  may  not  necessarily reside in the archive
X     directories, as long as the archives  know  where  they  are
X     located. See the man page for _f_a_r_c_h(_1) for more information.
X
X     Alternatively, messages may be  automatically  archived  via
X     the _a_r_c_h_i_v_e directive in the _c_o_n_f_i_g file.
X
XHHHHEEEELLLLPPPP
X     The system comes configured with  default  help  topics  the
X     recognized requests. More topics may be added by editing the
X     file  HOMEDIR/help/TOPICS,  adding  the  topic(s)  and   the
X     file(s) where the actual text exists.
X
X     Help files may be simple text files  or  shell  scripts,  in
X     which  case  they  have to start with "#!" in the first line
X     and be followed by the path to the shell  to  be  used;  for
X     example: #!/bin/sh
X
XSSSSYYYYSSSSTTTTEEEEMMMM MMMMAAAAIIIINNNNTTTTEEEENNNNAAAANNNNCCCCEEEE
X     As mentioned below, all programs report  to  certain  files.
X     The  system's  manager  should  periodically check the files
X
X
X
XAnastasios Kotsikonas                                              11
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/.report.server        and        the         various
X     HOMEDIR/lists/*/.report.list for any problems.  All messages
X     sent are saved under HOMEDIR/mbox  and  HOMEDIR/lists/*/mbox
X     and  they  should be cleaned up periodically.  For debugging
X     purposes, various warnings are written to  HOMEDIR/.warning;
X     this  file,  as  well  as HOMEDIR/.report.catmail are shrunk
X     every time _s_t_a_r_t(1) is run.
X
X     For whenever the necessity arises to administer  the  system
X     remotely,  the following scheme may be used to start or kill
X     the system, process the mail queue, force an application  to
X     execute immediately, etc: a mail message is sent to an alias
X     whose sole role is to fire up an application;  the  applica-
X     tion has to have been setuid before.
X
X     For instance, to start the system remotely, you may  set  up
X     an alias such as:
X
X       start-server: "|HOMEDIR/start -cr > /dev/null"
X
X     To kill the system, you may define another alias:
X
X       kill-server: "|HOMEDIR/start -crk > /dev/null"
X
X     To force requests to get processed now:
X
X       run-listserver: "|HOMEDIR/listserv -1 > /dev/null"
X
X     To start the queue daemon:
X
X       proc-queue: "|HOMEDIR/queued 60 & > /dev/null"
X
X     These aliases should be hard to guess for  obvious  reasons.
X     The actual message sent to such an alias is discarded.
X
XEEEEXXXXIIIITTTT CCCCOOOODDDDEEEESSSS
X     All applications except tlock use the following  exit  codes
X     for communication:
X
X
X     0    - OK, normal exit
X
X     1    - Could not open or lock file
X
X     2    - SIGINT signal (normal termination)
X
X     3    - Command line option error
X
X     4    - Syntax error in file
X
X     5    - Could not spawn (restart failed)
X
X
X
X
XAnastasios Kotsikonas                                              12
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     6    - Shutdown request
X
X     7    - Restart request
X
X     8    - Received system  signal  (anything  but  SIGINT  will
X          bring the system down)
X
X     9    - Too many multiple recipients
X
X     10   - Could not deliver mail (unexpected reply  during  the
X          SMTP transaction)
X
X     11   - malloc() failed
X
X     12   - Cannot fork()
X
X     13   - Socket connection problem
X
X     14   - Semaphore error
X
X     15   - Cannot setuid()/setgid()
X
X     16   - Internal error or system call failure
X
X     tlock exits with:
X
X     -1   - File locking not functional
X
X     0    - No files locked
X
X     1    - Cannot open config
X
X     2 & up
X          - Number of files locked + 1
X
XRRRREEEEPPPPOOOORRRRTTTTSSSS
X     The system provides two ways of reporting progress: via sys-
X     log(3)  when available (by compiling with -DSYSLOG=facility)
X     with priority LOG_INFO and facility as specified, or through
X     its own report files (when not using syslog(3)); these are:
X
X     HOMEDIR/.report.catmail
X          Messages from _c_a_t_m_a_i_l(1) every time  new  messages  are
X          appended to the various mail files.
X
X     HOMEDIR/.report.daemon
X          Messages  from  _s_e_r_v_e_r_d(1)  every  time  new  mail  has
X          arrived.
X
X     HOMEDIR/.report.list
X          Error messages from _l_i_s_t(1) when attempting to  process
X          public messages.
X
X
X
XAnastasios Kotsikonas                                              13
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/.report.pqueue
X          Messages from _p_q_u_e_u_e (see _q_u_e_u_e(1)).
X
X     HOMEDIR/.report.server
X          Messages from _l_i_s_t_s_e_r_v(1) every time new  requests  are
X          processed.
X
X     HOMEDIR/.report.start
X          Generated by _s_t_a_r_t(1) every time the system is started.
X
X     HOMEDIR/lists/LIST_ALIAS/.report.list
X          Messages from _l_i_s_t(1) when processing public messages.
X
X     HOMEDIR/.*.acc
X          Accumulated  reports  since  the   system   was   first
X          installed.
X
X     HOMEDIR/lists/*/.*.acc
X          Accumulated reports for each mailing list.
X
X     Every report includes a time stamp; every  list  and  server
X     report  also  includes the actual sender of the message, and
X     every server  report  includes  all  requests  made  by  the
X     sender.
X
X     Whenever a program dies abnormally (and BSD mail is present)
X     a  message  is  sent  to _m_a_n_a_g_e_r (as defined in _c_o_n_f_i_g). The
X     message is usually sent by _s_e_r_v_e_r_d(1) which then exits.  The
X     daemon  dies  with a different message according to the exit
X     status of the child; the various error conditions  that  may
X     occur are:
X
X     Could not open file
X          HOMEDIR is not a  valid  path;  file  was  accidentally
X          deleted;  insufficient access privileges. Run _s_e_t_u_p and
X          _s_t_a_r_t.
X
X     Could not lock file
X          HOMEDIR is not a  valid  path;  file  was  accidentally
X          deleted;  insufficient  access privileges; another pro-
X          gram is using the lock file. Run _t_l_o_c_k, and  if  neces-
X          sary,  _u_l_o_c_k;  then  _s_t_a_r_t.   _t_l_o_c_k  checks  for locked
X          files, and _u_l_o_c_k removes all lock files.
X
X     Command line option error
X          Check the _c_o_n_f_i_g file and restart.
X
X     Syntax error in file
X          Check _c_o_n_f_i_g, all  reports,  .subscribers,  .peers  and
X          .news files.
X
X     Could not spawn
X
X
X
XAnastasios Kotsikonas                                              14
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          No more processes.
X
X     Received system signal
X          SIGQUIT, SIGTERM, SIGBUS, SIGSEGV, SIGILL.
X
XFFFFIIIILLLLEEEESSSS
X     HOMEDIR/.aliases
X          Aliases of email addresses of users having trouble get-
X          ting replies to requests.
X
X     HOMEDIR/.awk
X          awk program for formatting the _s_t_a_t_i_s_t_i_c_s request.
X
X     HOMEDIR/.grep
X          script used for counting the number of messages sent by
X          a subscriber.
X
X     HOMEDIR/.ignored
X          List of unwanted senders.
X
X     HOMEDIR/.lock.list
X          Lock file for _l_i_s_t(1).
X
X     HOMEDIR/.lock.pqueue
X          Lock file for _p_q_u_e_u_e (see _q_u_e_u_e(1)).
X
X     HOMEDIR/.lock.server
X          Lock file for _l_i_s_t_s_e_r_v(1).
X
X     HOMEDIR/.lock.serverd
X          Lock file for _s_e_r_v_e_r_d(1).
X
X     HOMEDIR/.message.ids
X          A database of recent message id's used to  detect  mail
X          loops.
X
X     HOMEDIR/.queue.id
X          Next id to be assigned to  next  undeliverable  message
X          placed in the mqueue/ directory.
X
X     HOMEDIR/.reply.listser
X          The reply code given to _s_e_r_v_e_r_d(1) during a  live  ses-
X          sion.
X
X     HOMEDIR/.rep.server.acc
X          Archived _l_i_s_t_s_e_r_v(1) reports.
X
X     HOMEDIR/.rep.serverd.acc
X          Archived _s_e_r_v_e_r_d(1) reports.
X
X     HOMEDIR/.rep.start.acc
X          Archived _s_t_a_r_t(1) reports.
X
X
X
XAnastasios Kotsikonas                                              15
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/.report.catmail
X          Current _c_a_t_m_a_i_l(1) report.
X
X     HOMEDIR/.report.daemon
X          Current _s_e_r_v_e_r_d(1) report.
X
X     HOMEDIR/.report.list
X          Error messages from _l_i_s_t(1).
X
X     HOMEDIR/.report.server
X          Current _l_i_s_t_s_e_r_v(1) report.
X
X     HOMEDIR/.report.start
X          Last _s_t_a_r_t(1) action (system started or shut down).
X
X     HOMEDIR/.stats
X          Script used for the _s_t_a_t_i_s_t_i_c_s request.
X
X     HOMEDIR/.tag.id
X          Next id to be assigned to next moderated message.
X
X     HOMEDIR/.warning
X          Log of system calls' exit statuses.
X
X     HOMEDIR/batch
X          Batched user requests.
X
X     HOMEDIR/catmail
X          Incoming message redirection application.
X
X     HOMEDIR/config
X          The configuration file.
X
X     HOMEDIR/farch
X          File archiving utility.
X
X     HOMEDIR/iul
X          Interactive UNIX ListServer client; to be used to  con-
X          nect to the interactive part of the system.
X
X     HOMEDIR/list
X          The mailing list program.
X
X     HOMEDIR/listserv
X          The system's request server.
X
X     HOMEDIR/lost+found
X          Requests   that    could    not    be    appended    to
X          HOMEDIR/requests.
X
X     HOMEDIR/mbox
X          An archive of all requests sent to date to ListServer.
X
X
X
XAnastasios Kotsikonas                                              16
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/news
X          Script used to add a news group to a list.
X
X     HOMEDIR/owners
X          List of list owners' addresses used for  authentication
X          of list maintenance requests.
X
X     HOMEDIR/peer
X          Script used to add a peer list.
X
X     HOMEDIR/pqueue
X          The mail queue processing program (see _q_u_e_u_e(_1)).
X
X     HOMEDIR/priv.hosts
X          List of hosts that may connect to the interactive  part
X          of the system.
X
X     HOMEDIR/queued
X          The mail queue processing daemon (see _q_u_e_u_e(_1)).
X
X     HOMEDIR/received
X          File containing the  messages  received  from  sendmail
X          during  the  last  message  distribution  -- the _s_y_s_t_e_m
X          mailmethod has to be in use.
X
X     HOMEDIR/requests
X          Newly arrived user requests.
X
X     HOMEDIR/requests.live
X          Repository for requests coming  in  from  live  connec-
X          tions.
X
X     HOMEDIR/sent
X          File containing the messages sent  to  sendmail  during
X          the  last message distribution -- the _s_y_s_t_e_m mailmethod
X          has to be in use.
X
X     HOMEDIR/serverd
X          The system's daemon.
X
X     HOMEDIR/setup
X          System setup script.
X
X     HOMEDIR/start
X          The system's housekeeping program.
X
X     HOMEDIR/tlock
X          Checks for any locked files.
X
X     HOMEDIR/ulock
X          Script to remove all lock files.
X
X
X
X
XAnastasios Kotsikonas                                              17
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/unwanted.hosts
X          List of hosts that may not connect to  the  interactive
X          part of the system.
X
X     HOMEDIR/welcome.live
X          Welcoming message upon live connection establishment.
X
X     HOMEDIR/archives/listserver/DIR
X          Directory  of  all  files  available  from  the  master
X          archive (listserver).
X
X     HOMEDIR/archives/listserver/INDEX
X          The master archive index.
X
X     HOMEDIR/doc/catmail.1
X          Man page source for _c_a_t_m_a_i_l(1).
X
X     HOMEDIR/doc/catmail.nr
X          Formatted man page for _c_a_t_m_a_i_l(1).
X
X     HOMEDIR/doc/farch.1
X          Man page source for the _f_a_r_c_h(1) utility.
X
X     HOMEDIR/doc/farch.nr
X          Formatted man page for the _f_a_r_c_h(1) utility.
X
X     HOMEDIR/doc/iul.1
X          Man page source for the _i_u_l(1) utility.
X
X     HOMEDIR/doc/iul.nr
X          Formatted man page for _i_u_l(1).
X
X     HOMEDIR/doc/list.1
X          Man page source for _l_i_s_t(1).
X
X     HOMEDIR/doc/list.nr
X          Formatted man page for _l_i_s_t(1).
X
X     HOMEDIR/doc/listserv.1
X          Man page source for _l_i_s_t_s_e_r_v(1).
X
X     HOMEDIR/doc/listserv.nr
X          Formatted man page for _l_i_s_t_s_e_r_v(1).
X
X     HOMEDIR/doc/queue.1
X          Man page source for _p_q_u_e_u_e and _q_u_e_u_e_d(1).
X
X     HOMEDIR/doc/queue.nr
X          Formatted man page for _p_q_u_e_u_e and _q_u_e_u_e_d(1).
X
X     HOMEDIR/doc/server.1
X          Source of the system's main man page.
X
X
X
XAnastasios Kotsikonas                                              18
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     HOMEDIR/doc/server.nr
X          Formatted main man page.
X
X     HOMEDIR/doc/serverd.1
X          Man page source for _s_e_r_v_e_r_d(1).
X
X     HOMEDIR/doc/serverd.nr
X          Formatted man page for _s_e_r_v_e_r_d(1).
X
X     HOMEDIR/doc/start.1
X          Man page source for _s_t_a_r_t(1).
X
X     HOMEDIR/doc/start.nr
X          Formatted man page for _s_t_a_r_t(1).
X
X     HOMEDIR/help/*
X          General and topic-specific help files available upon  a
X          _h_e_l_p request. The TOPICS is a special file where topics
X          are defined.
X
X     HOMEDIR/lists/LIST_ALIAS/.aliases
X          Aliases of email addresses of subscribers.
X
X     HOMEDIR/lists/LIST_ALIAS/.digest.toc
X          All the subject lines from the digest. It will be writ-
X          ten at the head of the digest when it is sent.
X
X     HOMEDIR/lists/LIST_ALIAS/.digest.msg
X          All the messages of the  digest,  separated  by  dashed
X          lines.
X
X     HOMEDIR/lists/LIST_ALIAS/.digest.time
X          The time that the last digest was sent, represented  as
X          the  value returned by time(2). It is used to calculate
X          when it is time to send out the next digest.
X
X     HOMEDIR/lists/LIST_ALIAS/.headers
X          An archive of  who  sent  each  message;  used  upon  a
X          _s_t_a_t_i_s_t_i_c_s request.
X
X     HOMEDIR/lists/LIST_ALIAS/.ignored
X          A list of unwanted senders.
X
X     HOMEDIR/lists/LIST_ALIAS/.info
X          Text sent upon an _i_n_f_o_r_m_a_t_i_o_n request.
X
X     HOMEDIR/lists/LIST_ALIAS/.limits
X          Used by _s_e_r_v_e_r_d(1) when placing various limits for  the
X          list.
X
X     HOMEDIR/lists/LIST_ALIAS/.message.ids
X          A database of recent message id's used to  detect  mail
X
X
X
XAnastasios Kotsikonas                                              19
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          loops.
X
X     HOMEDIR/lists/LIST_ALIAS/.news
X          A list of all news groups connected to this list.
X
X     HOMEDIR/lists/LIST_ALIAS/.peers
X          A list of all peer lists for this mailing  list,  along
X          with the email addresses of their servers.
X
X     HOMEDIR/lists/LIST_ALIAS/.rep.list.acc
X          Archived reports from _l_i_s_t(1).
X
X     HOMEDIR/lists/LIST_ALIAS/.report.list
X          Current report from _l_i_s_t(1).
X
X     HOMEDIR/lists/LIST_ALIAS/.restricted
X          List of subscribers with alternate recipient files.
X
X     HOMEDIR/lists/LIST_ALIAS/.subscribers
X          A list of all subscribers along  with  preferences  and
X          settings.
X
X     HOMEDIR/lists/LIST_ALIAS/.un.digest
X          When a digest is constructed, it will be stored in this
X          file until it has been sent to all digest subscribers.
X
X     HOMEDIR/lists/LIST_ALIAS/.welcome
X          Text sent on a _s_u_b_s_c_r_i_b_e request.
X
X     HOMEDIR/lists/LIST_ALIAS/archive
X          Archive of all distributed messages since the file  was
X          last created.
X
X     HOMEDIR/lists/LIST_ALIAS/lost+found
X          Mail that could  not  be  appended  to  the  'mail'  or
X          'moderated' files (see next entries and _c_a_t_m_a_i_l(1)).
X
X     HOMEDIR/lists/LIST_ALIAS/mail
X          Newly arrived public messages to be distributed.
X
X     HOMEDIR/lists/LIST_ALIAS/mbox
X          An archive of all messages sent to date.
X
X     HOMEDIR/lists/LIST_ALIAS/moderated
X          Newly arrived public messages that need  to  be  edited
X          before distributed.
X
X     HOMEDIR/lists/LIST_ALIAS/removed.users
X          Entries from the .subscribers file for users  who  have
X          been automatically removed from the list.
X
X     HOMEDIR/lists/LIST_ALIAS/removed.alias
X
X
X
XAnastasios Kotsikonas                                              20
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          Entries from the .aliases file for users who have  been
X          automatically removed from the list.
X
X     HOMEDIR/mqueue/*
X          Queued messages for later delivery.
X
XUUUUPPPPGGGGRRRRAAAADDDDIIIINNNNGGGG
X     When upgrading, it is not necessary to remove  your  current
X     system.   Old  key directories and files will be moved else-
X     where. Always run the _s_e_t_u_p script. In addition, if you  are
X     upgrading from:
X
X     5.5  The system may now go interactive with the -i  flag  to
X          _s_e_r_v_e_r_d(1).
X
X          _r_e_m_o_t_e lists may now specify  host  names  or  Internet
X          addresses  and  ports if they are served by interactive
X          UNIX ListServers.
X
X          The ULISTSERVER_UMASK environment variable is now used.
X
X          The IUL (_i_u_l(_1)) client  is  available  to  connect  to
X          interactive UNIX ListServers.
X
X          The  _h_e_l_p _l_i_v_e  request  explains  how  to  connect  to
X          interactive UNIX ListServers.
X
X          The _p_r_e_c_e_d_e_n_c_e directive has been added to  the  _c_o_n_f_i_g
X          file.
X
X          Precious header lines are preserved for each  list  via
X          the _h_e_a_d_e_r directive.
X
X          Regular expressions are implemented for the  ".aliases"
X          and ".ignored" files.
X
X          Lists may now automaticlly archive distributed messages
X          via the _a_r_c_h_i_v_e directive.
X
X          The manager may allow subscribers of a list to  execute
X          UNIX  commands via the _u_n_i_x__c_m_d directive. Users submit
X          a _r_u_n request in this case (CCRUN is added as a prefer-
X          ence).
X
X          _f_a_r_c_h now creates new archives and takes care of updat-
X          ing  and creating DIR and INDEX files as well as direc-
X          tories, and may archive true  binary  files  (does  not
X          uuencode  them)  if  requested (-B flag); the -p option
X          was introduced to accept a password for a  new  private
X          archive,  the  -D option to specify a short description
X          of the files, and the -r option to  remove  files  from
X          archives.
X
X
X
XAnastasios Kotsikonas                                              21
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          Commands in the _c_o_n_f_i_g file may span multiple lines  if
X          each line is terminated by &\n.
X
X          The system intercepts requests sent to lists.
X
X          The _c_o_n_c_e_a_l user attribute has been introduced  to  the
X          subscribers files.
X
X          The _d_e_f_a_u_l_t directive has been introduced to the _c_o_n_f_i_g
X          file to set default values for new subscribers.
X
X          Users may change the address they are subscribed  with,
X          if the owner allows this.
X
X          The util/ directory has  been  introduced  for  general
X          utilities.
X
X          Help files may now  be  shell  scripts;  same  for  all
X          lists' ".welcome" and ".info" files.
X
X          syslog(3) is now supported for  progress  reporting  by
X          compiling with -DSYSLOG=facility.
X
X          Archive files may be split  on  the  way  out  if  they
X          exceed the _l_i_m_i_t specified in the _c_o_n_f_i_g file.
X
X          Lists may restrict the number of messages processed per
X          day with the _c_e_i_l_i_n_g directive.
X
X          The new request _f_a_x with its synonymous directive  have
X          been introduced.
X
X          Concealed lists are supported  via  the  -c  option  to
X          _l_i_s_t_s_e_r_v(1).
X
X          Error messages from  mailer  daemons  are  scanned  and
X          automatic   action   is   taken   if   compiling   with
X          -DERROR_MAIL_ANALYSIS=level (1=conservative, 9=full).
X
X          Archives may now be searched with the _s_e_a_r_c_h request.
X
X          Run the upgrade_to_6.0 script.
X
X     5.41 Moderated lists are now owner controlled,  rather  than
X          manager controlled.
X
X          Manager preferences have now been introduced.
X
X          See the UPGRADING section in farch(1).
X
X          Follow the upgrade instructions for 5.5
X
X
X
X
XAnastasios Kotsikonas                                              22
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     5.4  _c_a_t_m_a_i_l(1) has been implemented, therefore the  various
X          aliases  in  the  aliases file have to be redefined (as
X          explained above) to take advantage.
X
X          Owner preferences have been introduced  in  the  _o_w_n_e_r_s
X          file.
X
X          Follow the upgrade instructions for 5.41
X
X     5.31 The _l_i_s_t directive in the _c_o_n_f_i_g  file  now  takes  two
X          extra  arguments:  the  list  owner's  address, and the
X          list's access password.
X
X          _q_u_e_u_e_d now takes only one argument, a frequency count.
X
X          The system supports 10 lists by  default  (see  LIMITA-
X          TIONS  below);  if  you are currently using the maximum
X          supported    number    of    lists,     consult     the
X          HOMEDIR/src/defs.h file.
X
X          The layout  and  structure  of  INDEX  files  has  been
X          extended; see _f_a_r_c_h(_1) for more information.
X
X          Follow the upgrade instructions for 5.4
X
X     5.3  The _l_i_s_t directive in the _c_o_n_f_i_g  file  now  takes  two
X          extra  arguments:  the  list  owner's  address, and the
X          list's access password.
X
X          The system supports 10 lists by  default  (see  LIMITA-
X          TIONS  below);  if  you are currently using the maximum
X          supported    number    of    lists,     consult     the
X          HOMEDIR/src/defs.h file.
X
X          The layout  and  structure  of  INDEX  files  has  been
X          extended; see _f_a_r_c_h(_1) for more information.
X
X          Follow the upgrade instructions for 5.4
X
X     5.2  Use the _s_y_s_t_e_m mailmethod.
X
X          For     every     list     you     need     to     edit
X          HOMEDIR/lists/LIST_ALIAS/.ignored   and  add  one  more
X          entry: the full email address of the server account.
X
X          Follow the upgrade instructions for 5.3
X
X     5.0, 5.1
X          For     every     list     you     need     to     edit
X          HOMEDIR/lists/LIST_ALIAS/.ignored  and add two entries:
X          the actual list alias, and the full  email  address  of
X          this list.
X
X
X
XAnastasios Kotsikonas                                              23
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          _n_e_v_e_r_a_c_k is not a valid mail mode anymore. Change every
X          occurrence in ".subscribers" to NOACK, for every list.
X
X          Any peer lists and/or news groups appearing  in  ".sub-
X          scribers"  should  be  removed;  use  the _p_e_e_r and _n_e_w_s
X          scripts to put them back.
X
X          _s_e_r_v_e_r_d, _l_i_s_t and _l_i_s_t_s_e_r_v  no  longer  report  to  the
X          stdout by default -- use the -e command line option.
X
X          Follow the upgrade instructions for 5.2
X
XPPPPOOOORRRRTTTT SSSSPPPPEEEECCCCIIIIFFFFIIIICCCC
X     For various compilation options, consult  the  src/Makefile.
X     Starting with version 5.3, a universal mailmethod was intro-
X     duced: _s_y_s_t_e_m; It should be used in every  system  that  has
X     TCP/IP  installed,  as  it has been verified to work on many
X     systems below -- consult the src/README file.
X
X     IBM Risc
X          You should use _s_y_s_v__p_s as an option in the _c_o_n_f_i_g file,
X          and  compile with xlc -D_ALL_SOURCE -qnoro. You have to
X          use _s_y_s_t_e_m as the mailmethod.  Messages distributed  to
X          local  subscribers on the system may see their messages
X          arrive "From root...", but all other subscribers in the
X          outside  world  will  see "From listserver@..."  and/or
X          "From list@..." which is the correct header. If you use
X          _e_n_v__v_a_r _L_O_G_N_A_M_E /_b_i_n/_r_m_a_i_l  instead,  the behavior will
X          be reversed.  _t_e_l_n_e_t may not be used as  a  mailmethod.
X          Compile   with   -DHAVE_SELECT_H,  -DHAVE_ULIMIT_H  and
X          -DHAVE_SETJMP_H, and link with -lbsd.   In  defs.h  the
X          system is defined as having an SVR3 flavor.
X
X     SGI  You have to use the _s_y_s_t_e_m mailmethod.  All  Irix  ver-
X          sions  to date seem to have a serious problem with tel-
X          net (it corrupts file  descriptors).  Use  the  _s_y_s_v__p_s
X          option.   In  the  Makefile, define the symbol CC to be
X          'cc  -ansi'  or  'cc  -D__STDC__',  and  compile   with
X          -DHAVE_SETJMP_H  and  -D_POSIX_SOURCE.   In  defs.h the
X          system is defined as having an SVR3 flavor.
X
X     SUN  _s_y_s_t_e_m and _t_e_l_n_e_t work fine as  mailmethods.   Use  the
X          _b_s_d__p_s  option.  The  server  user id should not be the
X          same as root's.  If you use the native cc compiler  you
X          will  need  to  convert  to K&R C (by using the unproto
X          system  --   consult   the   src/Makefile);   otherwise
X          /usr/lang/acc  -w  (ANSI  C)  can be used. Compile with
X          -DHAVE_SETJMP_H.  In defs.h the system  is  defined  as
X          having  a  BSD flavor. On Solaris, compile with -Dsvr4.
X          If using the live part of the system and your host is a
X          YP/NIS  client  you  should  put the new service in the
X          master's services map, or if on the master, remake  the
X
X
X
XAnastasios Kotsikonas                                              24
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          map.  Older  versions of SUNOS have problems with their
X          include files, and in this case you have to comment out
X          the line:
X
X          #include <netdb.h>
X
X          in src/*.c
X
X     DECstations
X          You have to use _s_y_s_t_e_m as the mailmethod;  if  you  use
X          _t_e_l_n_e_t  instead,  you  may  have  to use the _b_a_d__t_e_l_n_e_t
X          option; _b_s_d__p_s should be used. Ultrix 3.0 has  lots  of
X          system  bugs  and  you may experience strange behavior;
X          the file locking mechanism over NFS appears to be  non-
X          standard and is disabled by default; upgrade to 4.2A is
X          recommended.  An ANSI C compiler should be used. If you
X          are using the unproto system, use the -nocpp flag to cc
X          when  compiling.  Compile  with  -DHAVE_SETJMP_H.    In
X          defs.h  the  system  is defined as having a BSD flavor.
X          All Bourne shell scripts may  need  to  be  altered  to
X          invoke /bin/sh5, before they are used.
X
X     Convex
X          It is suggested that you use the _s_t_d_s script (available
X          via anonymous ftp from cs.bu.edu) and the non-ANSI com-
X          piler; _s_t_d_s should be used as follows:
X
X          stds src/nonansi/*.h src/*.c
X
X          This should be done after redefining the HOMEDIR  entry
X          in  src/Makefile  and  running  setup  to  make all the
X          necessary changes. Compiling under ANSI standards (-std
X          flag)  is  not  recommended, since system include files
X          are not ANSI-C compliant. The system cannot go interac-
X          tive since the system does not support semaphores. _s_y_s_-
X          _t_e_m works fine as a mailmethod. _p_a_s_t_e(_1) is missing and
X          _s_t_a_t_i_s_t_i_c_s requests will look erroneous.
X
X     Stardent GS series
X          _s_y_s_t_e_m and _t_e_l_n_e_t work fine as  mailmethods.   Use  the
X          _s_y_s_v__p_s option. The unproto system has to be used (con-
X          sult the src/Makefile) if not using an ANSI C compiler.
X          The system cannot go interactive due to a system() bug.
X          In defs.h the system is defined as having an SVR3  fla-
X          vor.
X
X     Stardent/KPC Titan series
X          _s_y_s_t_e_m  and  _e_n_v__v_a_r _L_O_G_N_A_M_E /_b_i_n/_r_m_a_i_l  work  fine  as
X          mailmethods.  Use the _s_y_s_v__p_s option. The system cannot
X          go interactive due to the  absence  of  waitpid(),  and
X          compiling  and  linking with -43 (to use wait3()) hangs
X          _s_e_r_v_e_r_d(_1) and the other applications.  In  defs.h  the
X
X
X
XAnastasios Kotsikonas                                              25
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          system is defined as having an SVR3 flavor.
X
X     NeXT _t_e_l_n_e_t, _s_e_n_d_m_a_i_l, _s_y_s_t_e_m (if TCP/IP  is  installed  and
X          SMTP is used) work fine as mailmethods; however, 'mail-
X          method sendmail /usr/lib/sendmail -ba' has been charac-
X          terized  as  a  "DISASTER in terms of hogging the cpu."
X          Compile with  -D__NeXT__  if  your  compiler  does  not
X          define  this  symbol  (under version 3.0 of the OS this
X          symbol is automatically defined -- 3.0 is the only sup-
X          ported  environment).  Compile  with -I/usr/include/bsd
X          and -I/usr/include/bsd/sys.  File locking is turned off
X          because  of an OS bug.  In defs.h the system is defined
X          as having a BSD flavor. The system cannot  go  interac-
X          tive due to lack of semaphore support.
X
X     HP-UX
X          Compile  with  -Aa,  -D_HPUX_SOURCE,   -DHAVE_ULIMIT_H,
X          -DHAVE_SETJMP_H.   _s_y_s_t_e_m is the mailmethod to use.  In
X          defs.h the system is defined as having a BSD flavor.
X
X     SCO  Compile    with     -Dsco     and     possibly     with
X          -I/usr/include/netinet.   You  may  wish to link with a
X          special libbsd.a which can be obtained  from  /pub/sco-
X          ports/libbsd  on dribble.c-mols.siu.edu (131.230.93.2).
X          You need to link with -lsocket.  In defs.h  the  system
X          is  defined as having an SVR3 flavor. The port was done
X          under SCO version 3.2.4. The interactive part seems  to
X          hang for no apparent reason.
X
X     Apollo
X          Use the _s_y_s_t_e_m mailmethod. Do not compile with -A ansi,
X          or  if  you  do also compile with -Dapollo. The OS that
X          the port was performed under was SR10.4 and version 6.8
X          of   cc.   The   server   id's   path   should  include
X          /sys5.3/usr/bin so that 'paste' may be found. In defs.h
X          the  system is defined as having a BSD flavor. The sys-
X          tem cannot go interactive due to lack of semaphore sup-
X          port.
X
X     Xenix
X          Compile with -Dxenix; grep(1) is buggy and is suggested
X          that  another  version is used. In defs.h the system is
X          defined as having an SVR3 flavor.
X
X     Sequent
X          Compile with gcc -fwritable-strings  and  -Dsequent  if
X          the compiler does not define this symbol. In defs.h the
X          system is given a BSD flavor. If under Dynix/PTX,  com-
X          pile  with -Dsvr3 (PTX is a a System V R3.2 implementa-
X          tion; V2.0.3 includes an ANSI C compiler).
X
X     Data General
X
X
X
XAnastasios Kotsikonas                                              26
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          Compile using gcc  -fwritable-strings;  in  defs.h  the
X          system  is given an SVR4 flavor and is advisable not to
X          give it a BSD one; keep in mind that signals frequently
X          get lost.
X
X     i860 This is a standard SVR4.2 UNIX; _s_y_s_t_e_m  works  fine  as
X          mailmethod. All features are functional.
X
X     i386 This  is  a  BSD  environment;  the  system  cannot  go
X          interactive  due to lack of semaphore support, and file
X          locking is turned off. _s_y_s_t_e_m works fine as mailmethod.
X          Compile with the -fwritable-strings flag.
X
X     OSF  This is a standard SVR3  UNIX;  _s_y_s_t_e_m  works  fine  as
X          mailmethod.  All  features are functional. Compile with
X          the -std1 flag. Do  not  compile  with  -DHAVE_SELECT_H
X          because of missing system header files.
X
X     Other
X          First, attempt compiling with -Dunknown_port.
X
XLLLLIIIIMMMMIIIITTTTAAAATTTTIIIIOOOONNNNSSSS
X     - Up to 10 mailing lists can be supported  (configurable  in
X     src/defs.h and src/Makefile).
X
X     - The system assumes a length of 32 bits or higher for  long
X     integers.
X
X     - A list alias may not be called "server".
X
XCCCCOOOOMMMMPPPPIIIILLLLEEEERRRR
X     gcc  If you are using gcc to compile,  use  the  -fwritable-
X          strings  flag.  Versions prior to 2.2.1 may cause prob-
X          lems.
X
XMMMMAAAAIIIILLLLEEEERRRR
X     Zmailer
X          If you are using the Zmailer to send mail, or any other
X          mailer  (like Smail) that requires a 'HELO hostname' to
X          start the  SMTP  transaction,  compile  with  -DZMAILER
X          (also see src/README and src/Makefile).
X
XRRRREEEEQQQQUUUUIIIIRRRREEEEMMMMEEEENNNNTTTTSSSS
X     The $path for the server account should include  all  neces-
X     sary  paths  to cut(1), paste(1), awk(1), grep(1), uptime(1)
X     and telnet(1).  In addition,  uptime(1)  should  report  the
X     number  of  users  and the system load; otherwise _s_e_r_v_e_r_d(1)
X     cannot be run with the -l option, and _l_i_s_t_s_e_r_v(1) cannot  be
X     run  with  the -r option. The output from uptime should look
X     like:
X
X     12:45pm up 5 days, 16 mins, 4 users, load average: 0.00, ...
X
X
X
XAnastasios Kotsikonas                                              27
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X     Also,  make  sure  that  when  posting,  /usr/lib/news/_i_n_e_w_s
X     exists  either  as  itself  or  as  a link to wherever _i_n_e_w_s
X     resides (also see src/Makefile).
X
X     Finally, grep(1) should support  the  -i  (case  insensitive
X     matching)  and  -v  (print  all  lines that do not match the
X     expression) flags.
X
XBBBBUUUUGGGGSSSS
X     uptime
X          When user and load restrictions are enforced, the  sys-
X          tem  assumes  that uptime outputs a line similar to the
X          one shown above, in which the number of  users  is  the
X          third  column (columns are separated by commas) and the
X          load average is the fourth column. The problem is  that
X          on  a  system  that  has been up for less than one day,
X          uptime will report the number of users  in  the  second
X          column,  and  the  load average in the third column. In
X          this case, such restrictions should not be enforced.
X
X     blanks
X          Although the system handles email addresses with blanks
X          in   them   fairly  well,  _s_t_a_t_i_s_t_i_c_s,  _r_e_c_i_p_i_e_n_t_s  and
X          _s_e_t _l_i_s_t (status query) requests  will  show  erroneous
X          results for these users.
X
X     special characters
X          The system does not accept addresses that contain  sin-
X          gle quotes, back-quotes or wild characters.
X
X     messages exceeding limits not caught
X          When all subscribers of a list have set their mail mode
X          to  _d_i_g_e_s_t,  messages  are  not subject to size limita-
X          tions.
X
X     no _s_t_a_t_i_s_t_i_c_s request for NeXT 2.1 hosts
X          Due to the absense of _c_u_t(_1) the ._s_t_a_t_s script  has  to
X          be modified to replace the use of _c_u_t(_1) with _a_w_k(_1).
X
X     _s_e_r_v_e_r_d(1) dies with 'no tty' error message
X          In the _c_o_n_f_i_g file use the -e flag in the _l_i_s_t,  _s_e_r_v_e_r
X          and _s_e_r_v_e_r_d definitions followed by '> /dev/null 2>&1'.
X          For example:
X
X                    serverd -i 600 -e > /dev/null 2>&1
X
X     incorrect auto-splitting of files on the way out
X          When splitting files that exceed the  specified  limit,
X          the  system  precalculates  the number of parts it will
X          send and puts this number in the Subject:  line.   How-
X          ever,  since  each  part may actually be less than that
X          limit (each part extends to the nearest newline without
X
X
X
XAnastasios Kotsikonas                                              28
X
X
X
X
X
X
Xserver(1)                USER COMMANDS                  server(1)
X
X
X
X          exceeding  the limit) it is possible to send more parts
X          than predicted, if the  total  number  of  bytes  back-
X          tracked  (for  all parts) is an integer multiple of the
X          limit; in this case the number of extra parts  will  be
X          this  integer multiple. The probability of this happen-
X          ing increases as the limit is decreased.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     catmail(1),   farch(1),   iul(1),   list(1),    listserv(1),
X     queue(1), serverd(1), start(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                              29
X
X
X
*-*-END-of-doc/server.nr-*-*
echo x - doc/serverd.nr
sed 's/^X//' >doc/serverd.nr <<'*-*-END-of-doc/serverd.nr-*-*'
X
X
X
Xserverd(1)               USER COMMANDS                 serverd(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     sssseeeerrrrvvvveeeerrrrdddd - UNIX ListServer system daemon
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     sssseeeerrrrvvvveeeerrrrdddd [----1111] [----eeee] [----llll llllooooaaaadddd] [----iiii dddduuuurrrraaaattttiiiioooonnnn]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN::::
X     _s_e_r_v_e_r_d silently looks for any new messages for the  request
X     server  and  for  any of the supported mailing lists, checks
X     whether the time has arrived for processing the batch queue,
X     whether  digest time has been reached for the various lists,
X     and whether any live TCP/IP connections  are  requested.  It
X     then  spawns  _l_i_s_t_s_e_r_v(1)  and/or  _l_i_s_t(1) as necessary, and
X     sleeps until the  message(s)  is  (are)  processed.  _s_e_r_v_e_r_d
X     reads the _c_o_n_f_i_g file (see _s_e_r_v_e_r(1)).
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -1   Execute only once -- to be used with cron(1).
X
X     -e   Echo reports to the screen; it has  no  effect  if  the
X          system has been compiled with -DSYSLOG.
X
X     -l load
X          Enforce load restrictions; _s_e_r_v_e_r_d will postpone spawn-
X          ing if the system load is above the limit specified.
X
X     -i duration
X          A child process is also spawned which listens for  live
X          TCP/IP  connections at port 372 (this is referred to as
X          the Listsener process, and its parent as  the  Master);
X          for  each such connection a child process is created to
X          process     the     live     requests     (see      the
X          INTERACTIVE LISTSERVER   section  below).  The  maximum
X          _d_u_r_a_t_i_o_n time is given in seconds.
X
XIIIINNNNTTTTEEEERRRRAAAACCCCTTTTIIIIVVVVEEEE LLLLIIIISSSSTTTTSSSSEEEERRRRVVVVEEEERRRR
X     Provided that the host supports  the  TCP/IP  protocol,  the
X     system  provides a mechanism for processing live requests by
X     specifying the -i flag to _s_e_r_v_e_r_d. Any  user  may  use  _t_e_l_-
X     _n_e_t(_1)  to  connect  to a host that runs this system at port
X     number 372, issue requests as he would by email, and receive
X     responses immediately. However, the proper way to connect to
X     the server is by using _i_u_l(_1) which lets the  user  download
X     files  through _g_e_t requests, and provides other amenities as
X     well.
X
X     What requests are allowed depends on the privileges assigned
X     upon  login: the user is asked for an email address (default
X     is no email address); then a password should be provided. If
X     the  user  needs  manager privileges, he has to provide both
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xserverd(1)               USER COMMANDS                 serverd(1)
X
X
X
X     the manager's email address and  the  system's  password  as
X     they   appear  in  the  _c_o_n_f_i_g  file.  If  he  wishes  owner
X     privileges, he has to provide both his email address  as  it
X     appears  in  the  _o_w_n_e_r_s  file and his list's password as it
X     appears in the _c_o_n_f_i_g file. To gain subscriber privileges he
X     has  to  provide his email address as subscribed to the list
X     where privileges are to be granted to, as well as the  pass-
X     word _s_e_t for that list.
X
X     A mismatch or no  email  address  reduces  privileges  to  a
X     minimum.  These  are  restricted to _h_e_l_p, _i_n_f_o_r_m_a_t_i_o_n, _r_e_c_i_-
X     _p_i_e_n_t_s and _s_t_a_t_i_s_t_i_c_s for nonprivate  lists,  _l_i_s_t_s,  _i_n_d_e_x,
X     _g_e_t,  _r_u_n and _r_e_l_e_a_s_e requests. Subscriber privileges extend
X     these to _s_e_t, _r_u_n, _u_n_s_u_b_s_c_r_i_b_e and _w_h_i_c_h  requests.   Owners
X     are  granted permission to also issue _s_y_s_t_e_m, _r_e_p_o_r_t_s, _e_d_i_t,
X     _p_u_t, _a_p_p_r_o_v_e and _d_i_s_c_a_r_d requests in addition  to  the  ones
X     above.  Finally,  the  _m_a_n_a_g_e_r  may issue any request except
X     _e_x_e_c_u_t_e.
X
X     Requests for remote lists are serviced by attempting to con-
X     nect to the remote servers serving these lists.
X
X     The system may restrict connections to certain hosts, and/or
X     reject  connections  from  certain  hosts. First, the system
X     looks for the file _p_r_i_v._h_o_s_t_s which lists  hostnames  and/or
X     IP  addresses  that are allowed access; only hosts listed in
X     this file are granted acces. For example, to allow access to
X     hosts on one's local network only, one could do:
X
X                 ln -s /etc/hosts HOMEDIR/priv.hosts
X
X     Next, the file _u_n_w_a_n_t_e_d._h_o_s_t_s is looked up, and  connections
X     from  hosts  listed  in it are rejected. Regular expressions
X     may be used in both files.
X
X     The duration of each connection is limited to the number  of
X     seconds  given  as  argument  to the -i flag. A maximum of 5
X     simultaneous connections can be accepted at  any  time.  All
X     requests  are  serialized and the server may be kept busy if
X     it has not finished sending data to a socket  (for  example,
X     when  a user pipes the output he receives to more(1) and the
X     amount of data to be sent is  more  than  the  the  socket's
X     buffer).
X
X     To set up your system to accept such connections,  the  fol-
X     lowing   line   has   to   be   inserted  into  your  host's
X     /etc/services file:
X
X                         ulistserv   372/tcp
X
X     This port is privileged, therefore _s_e_r_v_e_r_d has to be started
X     with  root privileges (the _s_e_t_u_p script takes care of this).
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xserverd(1)               USER COMMANDS                 serverd(1)
X
X
X
X     If you do not have root access you may choose any other port
X     number  above  1024,  and  in  this case _s_e_r_v_e_r_d will give a
X     warning every time it starts, which can be ignored.
X
X     Consult the src/README file for qualification tests for your
X     system  to  go  interactive and the Makefile for compilation
X     options; also see the PORT SPECIFIC  section  in  _s_e_r_v_e_r(1).
X     Lastly, the file HOMEDIR/welcome.live is copied upon log in,
X     and it may be modified.
X
X     If your setup uses a name server you may wish to  link  with
X     libraries that provide DNS support.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     iul(1), list(1), listserv(1), queue(1), server(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
*-*-END-of-doc/serverd.nr-*-*
echo x - doc/start.nr
sed 's/^X//' >doc/start.nr <<'*-*-END-of-doc/start.nr-*-*'
X
X
X
Xstart(1)                 USER COMMANDS                   start(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     ssssttttaaaarrrrtttt - start/stop the UNIX ListServer system
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     ssssttttaaaarrrrtttt [----cccc] [----rrrr] [----kkkk]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _s_t_a_r_t is used to start or stop the UNIX  ListServer  system.
X     When  starting  the  system,  any  previous server processes
X     still running are  killed,  all  necessary  lock  files  are
X     created,  any  previous reports are archived into files with
X     extension ._a_c_c, new files and directories  are  created  for
X     any  new  mailing  lists,  and  _s_e_r_v_e_r_d(1) is spawned. _s_t_a_r_t
X     reads the _c_o_n_f_i_g file (see server(1)), and should always  be
X     run from the server account (also see _s_e_r_v_e_r(1)).
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following command line options are recognized:
X
X     -c   Suppress confirmation when killing  processes  or  when
X          creating new mailing directories and files.
X
X     -k   Just kill any old server system processes and exit.
X
X     -r   Restrict reporting to stdout.
X
XSSSSEEEEEEEE AAAALLLLSSSSOOOO
X     server(1), serverd(1)
X
XAAAAUUUUTTTTHHHHOOOORRRR
X     Anastasios C. Kotsikonas
X     Copyright (c) 1991-93, Anastasios Kotsikonas
X     Comments to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
*-*-END-of-doc/start.nr-*-*
echo x - doc/README
sed 's/^X//' >doc/README <<'*-*-END-of-doc/README-*-*'
X
XTo install the man pages permanently on your system you will need to do the
Xfollowing as superuser:
X
X% cp *.nr /usr/man/cat1 <- or wherever the man pages are
X% cd /usr/man/cat1
X% compress *.nr
X% mv server.nr.Z server.1
X% mv farch.nr.Z farch.1
X% mv queue.nr.Z queue.1
X% mv catmail.nr.Z catmail.1
X% mv list.nr.Z list.1
X% mv listserv.nr.Z listserv.1
X% mv serverd.nr.Z serverd.1
X% mv start.nr.Z start.1
X% mv iul.nr.Z iul.1
X% chmod 444 server.1 farch.1 queue.1 catmail.1 list.1 listserv.1 serverd.1 \
X  start.1 iul.1
X% vi Index <--- if availbable
X  add the following lines in Index in the appropriate alphabetic location:
X	server.1:server:
X	farch.1:farch:
X	queue.1:queue:
X	catmail.1:catmail:
X	list.1:list:
X	listserv.1:listserv:
X	serverd.1:serverd:
X	start.1:start:
X	iul.1:iul:
X% vi /usr/man/whatis <-- or whatever file is used when doing man -k
X  add the following lines in whatis:
X	server(1)	- UNIX ListServer system description
X	farch(1)	- UNIX ListServer file archiving utility
X	queue(1)	- UNIX ListServer mail queue processing
X	catmail(1)	- UNIX ListServer mail redirection
X	list(1)		- UNIX ListServer mailing list processing
X	listserv(1)	- UNIX ListServer request processing
X	serverd(1)	- UNIX ListServer system daemon
X	start(1)	- UNIX ListServer housekeeper
X	iul(1)		- Interactive UNIX ListServer client
X
*-*-END-of-doc/README-*-*
echo x - src/README
sed 's/^X//' >src/README <<'*-*-END-of-src/README-*-*'
X
X
X                    	UNIX LISTSERVER SYSTEM
X                    	----------------------
X
X           Copyright (c) 1991-93, Anastasios C. Kotsikonas
X
X                           tasos@cs.bu.edu
X			     June 1 1993
X
XDESCRIPTION:
X     This is a system that implements various mailing lists  with
X     one  list  server. It is automated, and obliterates the need
X     for user intervention and maintenance of multiple aliases of
X     the  form  "list,  list-owner,  list-request", etc. There is
X     support  provided  for  public  and   private   hierarchical
X     archives,  moderated  and  non-moderated  lists, peer lists,
X     peer servers, private lists, address aliasing, news  connec-
X     tions  and gateways, mail queueing, digests, list ownership,
X     owner preferences, crash recovery, batch processing,  confi-
X     gurable headers, regular expressions, archive searching, and
X     live user connections via TCP/IP.
X
XCOPYING: You may distribute the software freely, but you may not include
Xany part of the code in a commercial application whatsoever, directly or by
Ximplication. Modified code may NOT be distributed.
X
XAGREEMENT: This software can be used and distributed freely only as a
Xwhole and not in parts, as long as you do not remove or alter the author
Xand copyright notices in the file defs.h; this notices are #define'd in
Xthe symbols VERSION and COPYRIGHT. Although you may alter the code
Xprovided for your personal use, you you may not alter the functions
Xcreate_header(), create_multi_recipient_header() and main() in list.c,
Xlistserv.c and serverd.c (where applicable), and you may not redistribute
Xany changes you may have made. No part of the source code bearing a
Xcopyright notice can be included in commercial software systems without
Xwritten permission by the author.
XBy using this software you are bound by this agreement. 
XThis software comes with no warranties and cannot be sold for profit.
XThe AGREEMENT and COPYRIGHT notices should be included in all source
Xfiles when distributing this software.
XCOPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
XUse, duplication or disclosure by the Federal Government is subject to the
Xrestrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
Xfor Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
XUPDATES: All updates can be obtained from cs.bu.edu (128.197.10.1) via
Xanonymous ftp, in the directory pub/listserv. If you downloaded this version
Xfrom another ftp site, please keep an eye on cs.bu.edu. Bug fixes will
Xbe posted periodically over there, and no notifications will be posted to
Xnews; you should check the directory periodically.
X
XCOMPILER: The programs are written to be ANSI C compliant (almost); if your
Xcompiler cannot handle them, I suggest you also download unproto.tar.Z
Xfrom cs.bu.edu; cd pub/unproto; this is a nice set of programs that will
Xconvert ANSI-isms to old style C -- please send any questions about unproto
Xto its author, not me. In this case, follow the instructions in the Makefile
Xin the src directory for adjustments to be able to use this package.
XFor those compilers that die when they see a contol-L in the source code, I
Xhave written a C program that "cleans" source files; it is called clean.c and
Xcan be found in pub/utils as well; compile as follows:
X		% cc -Dunix clean.c -o clean
XThe compiler should support symbol names longer than 8 characters. Also consult
Xthe Makefile, as well as the PORT SPECIFIC section in ../doc/server.1
X
XLINKING: If you are using the interactive part of ListServer and your
Xsetup uses a name server, you may wish to link with libraries that provide
XDNS support (for example, on SYSV R4 compile with -lsockdns instead of
X-lsocket).
X
XPREPROCESSOR: Strings are defined using both ANSI C specifications and
Xnon-ANSI standards, so that unproto will work correctly (unproto 2.0 does not
Xhandle ANSI string concatenations). Which ones are used during compilation 
Xdepends whether the macro __STDC__ is defined by the compiler/preprocessor.
XNote: the script 'stds' is now rarely needed and can be obtained from cs.bu.edu
Xin the directory pub/listserv/utils.
X
XSOURCE CODE MODIFICATIONS: If you modify the code to work around problems
Xwith your operating system, or enhance the system, please send me diff's
Xto be incorporated in the next release.
X
XTCP/IP SUPPORT: The 'system' mailmethod and the live ListServer require support
Xfor Berkeley sockets and internet support. The system defines the macro TCP_IP
Xin defs.h. Remove this definition if your host does not support sockets, etc.
XFailure to do so will lead to compiler errors.
X
XSEMAPHORE/SIGNAL SUPPORT: The interactive part of the system depends on
Xsemaphore support. If your host does not support semaphores, undefine the
Xsymbol GO_INTERACTIVE in defs.h; this symbol is automatically undefined if
XTCP_IP is not defined, or the SIGCLD/SIGCHLD, SIGUSR1 and SIGUSR2 signals are
Xnot present. The system uses the POSIX waipid() function which may not be
Xavailable on your system (like Sequent for example). You may use wait3()
Xinstead; see serverd.c, sighandle(). If you use wait3() and its first argument
Xwhould be a union, then compile with -DWAIT3_NEEDS_UNION (integer is used by
Xdefault).
X
XSPECIAL HEADER FILES: Platforms that have the <sys/select.h>, <ulimit.h>
Xand/or <setjmp.h> header files should use -DHAVE_SELECT_H -DHAVE_ULIMIT_H 
Xand -DHAVE_SETJMP_H when compiling.
X
XSIGNALS: Various UNIX versions deal with signals in a number of ways.
XIf your UNIX is strict SYSVR4 compile with -Dsvr4 -- SYSVR4 uses sighold() and
Xsigrelse(); if it is SYSVR3 compile with -Dsvr3 -- the same functions are
Xdefined; if it is strict BSD based compile with -Dbsd -- BSD uses
Xsigblock() and sigsetmask(). If your system does not define either of these
Xsets of routines or is not a strict implementation of the above, do not
Xuse the above flags and certain signals will be ignored at certain times
Xdue to an old-UNIX bug with system(). In defs.h certain ports are given
Xpredefined flavors.
X
XNON-BLOCKING I/O: The system employs a timeout mechanism that works
Xonly with the 'system' mailmethod, provided that the host supports
Xnon-blocking I/O; if it does not, undefine the symbol NONBLOCKING_IO
Xin defs.h (defined by default).
X
XLOCKS: The system relies heavily on locks; however, a number of systems
Xdo not support locking across NFS mounted file systems, or do not support
Xlocks at all. If you are experiencing locking problems, define the symbol
XNO_LOCKS in defs.h (or compile with -DNO_LOCKS) and pray, or move the system
Xto a local file system. The following ports have NO_LOCKS automatically
Xdefined: Minix, Ultrix, NeXT, Apollo, Xenix, Convex.
X
XSYSLOG: You may use syslog(3) for reporting if you compile with
X-DSYSLOG=facility.
X
XUSING ANOTHER HOST TO SEND MAIL: By default, the 'system' mailmethod connects
Xto "localhost" for mail delivery; you may use another host by altering the
Xsymbol SENDMAIL_HOST in sysmail.h
X
XSUBSCRIPTION SUSPENSION: The system scans error messages looking for addresses
Xto suspend sending mail to, and it allows by default a 7 day grace period. This
Xperiod may be changed by altering list.h or by compiling with
X-DGRACE_PERIOD=time_in_seconds.
X
XErrors-To: lines: If you do not want the system to put Errors-To: lines 
Xcompile with -DNO_ERRORS_TO.
X
XZMAILER and other non-sendmail mailers: If your mailer requires a
X'HELO hostname' or 'HELO ip-address' greeting, then compile with -DZMAILER.
XIf TCP/IP is available, the host's name and IP address are obtained with system
Xcalls. If not, they default to "localhost" and "127.0.0.1" (see defs.h) and
Xthey have to redefined. By default, the hostname is included in the greeting.
XIf the IP address is required, see list.c and listserv.c.
X
XUPGRADING: If you are upgrading from an earlier version, take a look at the
XUPGRADING and PORT SPECIFIC sections in ../doc/server.nr and run the
Xupgrade_to_6.0 shell script.
X
XPORTS: The code has been ported to the following UNIXes:
XSUN, IBM, SGI, DEC, HP, Convex, Stardent, KPC, NeXT, SCO, Apollo, Sequent, Data
XGeneral, i860, i386 (bsd) and OSF. If you are on a different host, try first
Xcompiling with -Dunknown_port.
X
XINSTALLATION DIRECTORY: Change the HOMEDIR symbol in Makefile to be the top
Xlevel directory where this system is installed.
X
XFORUM: You should join our forum at unix-listserv@stormking.com where we
Xdiscuss problems and enhancements about this system; send a subscribe request
Xto listserv@stormking.com
X
XACKNOWLEDGEMENTS: I would like to thank the following people for their
Xsuggestions and contributions to this system (not all suggestions have been
Xincorporated):
X1) Bob Boyd (rbn@epavax.rtpnc.epa.gov): IRIX port, gateway connections
X   feedback, 5.3 beta testing. [Since Dec. 1992 Bob is no longer at EPA 
X   and I wish him good luck]
X2) Stefan Schroer (Stefan.Schroer@cyber.urz.uni-wuppertal.dbp.de): MINIX port
X   (code not reported since 5.0), help files, original 'get' and 'index'
X   requests.
X3) Scott J. Ellentuch (tuc@stormking.com): IBM R6000 port, enhancements to
X   the farch utility, 5.3&up beta testing, setting up of the unix-listserv
X   forum, endless support on the net -- he is my buddy.
X4) David Warner (warner@austin.onu.edu): IBM R6000 bug fixing, 5.3 beta 
X   testing, tolerance with my obnoxious code -- I owe this gentleman a lot.
X5) Nathan F. Estey (nestey@copper.Denver.Colorado.EDU): 5.4 suggestions.
X6) Aad Nienhuis (aad@sconar.sco.uva.nl) for the catmail utility.
X7) Warren Burstein (warren@itexjct.jct.ac.il): digests, owner moderated lists
X   (listserv sends messages to owner, owner sends back the approved ones --
X   my scheme uses APPROVE and DISCARD requests), significant code rewriting,
X   manager preferences, automatic archiving of messages.
X8) Nicos Kontopoulos (nicos@cs.bu.edu) for a lot of tiny suggestions.
X9) Matthias Klose <doko@cs.tu-berlin.de>, John Neil <neil@dehn.mth.pdx.edu>
X   for the NeXT port.
X10) Ken Mayer (ken@visix.com) for the Precedence: stuff.
X11) Henry Spencer for the pattern matching routines.
X12) Carlos O'Ryan Lira (coryan@mat.puc.cl) for the Apollo port.
X13) Anand V Raman (A.Raman@massey.ac.nz) for modifying 'setup' to rebuild the
X    system under the HOMEDIR definition in Makefile.
X14) Kenneth Lorber (keni@oasys.dt.navy.mil) for providing fio.c and speeding up
X    the system by reducing the number of syscom()'s.
X15) All of those of you who made literally a myriad of small suggestions here
X    and there, and contributed even a single character of code, and especially
X    all of you who let me into your systems to do the ports myself.
X
X            The UNIX ListServer system includes 157 files:
X
X HOMEDIR/doc:
X   *.nr		-- proformatted man pages
X   catmail.1	-- man page for the catmail utility
X   farch.1	-- man page for the file archiving utility
X   iul.1	-- man page for the Interactive UNIX ListServer client
X   list.1	-- man page for the mailing list processing application
X   listserv.1	-- man page for the request processing application
X   queue.1	-- man page for the mail queue processing application
X   server.1	-- man page describing the system
X   serverd.1	-- man page for the system daemon
X   start.1	-- man page for the system's housekeeper
X   README	-- instructions for installing the man pages on your system
X
X HOMEDIR/help:
X   TOPICS	-- index file with topics and corresponding filenames
X   general      -- general help file
X   approve	-- help on the approve request
X   discard	-- help on the discard request
X   information  -- help on the information request
X   lists        -- help on the lists request
X   recipients   -- help on the recipients request
X   set          -- help on the set request
X   statistics   -- help on the statistics request
X   subscribe    -- help on the subscribe request
X   unsubscribe  -- help on the unsubscribe request
X   index	-- help on the index request
X   get		-- help on the get request
X   release	-- help on the release request
X   which	-- help on the which request
X   reports	-- help on the reports request
X   edit		-- help on the edit request
X   put		-- help on the put request
X   live		-- help on how to access the live ListServer
X   listserv	-- help on this system
X   fax		-- help on the fax request
X   search	-- help on the search request
X
X HOMEDIR/archives:
X   HOMEDIR/archives/listserver: Master archive:
X     DIR	-- info on files and directories they are located
X     INDEX	-- master index of all archives (listserver, pub, unix)
X     refcard	-- list of all commands
X     info	-- info on this archive
X     HOMEDIR/archives/listserver/example.dat:
X       example.dat1	-- part 1 of file example.dat in archive listserver
X       example.dat2	-- part 2 of file example.dat in archive listserver
X       example.dat3	-- part 3 of file example.dat in archive listserver
X   HOMEDIR/archives/iul: Source files for the IUL client:
X     DIR	-- info on files in this subdirectory
X     INDEX	-- index of all archives in this archive
X     iul.c	-- the IUL client
X     iul.h	-- specific definitions for iul.h
X     iulp.h	-- definition of IUL Protocol
X     iul.1	-- (as before)
X     iul.nr	-- (as before)
X     makefile	-- makefile to build the IUL client
X   HOMEDIR/archives/pub: Subarchive of listserver:
X     DIR	-- info on files and directories they are located
X     INDEX	-- index of all archives in this archive
X     info	-- info on this archive
X   HOMEDIR/archives/unix: Subarchive of listserver:
X     DIR	-- info on files and directories they are located
X     INDEX	-- index of all archives in this archive
X     info	-- info on this archive
X   HOMEDIR/archives/pub/unix: Subarchive of pub (duplicate name example):
X     DIR	-- info on files and directories they are located
X     INDEX	-- index of files in this archive
X     info	-- info on this archive
X   HOMEDIR/archives/pub/private: Subarchive of pub; example private archive
X     DIR	-- info on files and directories they are located
X     INDEX	-- index of files in this archive
X     info	-- info on this archive
X
X HOMEDIR/src:
X   REGISTRATION	-- registration form for using this software
X   README	-- this file
X   global.h	-- global variables definitions
X   struct.h	-- defines the server structure
X   defs.h	-- general definitions
X   tlock.c	-- tests for locks, i.e. whether any server programs are running
X  		   on another file system via NFS
X   list.h	-- specific definitions for list.c
X   list.c	-- the list's server
X   listserv.h	-- specific definitions for listserv.c
X   listserv.c	-- server for individual requests
X   pqueue.h	-- specific definitions for pqueue.c
X   pqueue.c	-- processes the mail queue
X   serverd.h	-- specific definitions for serverd.c
X   serverd.c	-- parent program that spawns list or listserv
X   start.h	-- specific definitions for start.c
X   start.c	-- does housekeeping before spawning serverd, makes sure that
X		   files exist, kills any running server processes, etc
X   sysmail.h	-- specific definitions for sysmail.c
X   sysmail.c	-- system mailmethod routines
X   catmail.h	-- specific definitions for catmail.c
X   catmail.c	-- append incoming mail to the appropriate file
X   signals.c	-- signal processing routines
X   sender.c	-- sender address manipulation routines
X   misc.c	-- general purpose routines
X   farch.c	-- utility for easy archiving of files
X   next.h	-- specific definitions for NeXT hosts
X   sem.c	-- semaphore definitions for serverd's interactive part
X   rev.c	-- source to BSD 'rev'
X   iulp.h	-- Interactive UNIX ListServer Protocol definition
X   iul.h	-- specific definitions for iul.c
X   iul.c	-- client application to connect to an interactive system
X   siul.c	-- system iul used by ListServer to connect to remote servers
X   regex.c	-- fornt end to pattern matching routines
X   regerror.c	-- pattern matching routines -- Henry Spencer
X   regexp.c	-- pattern matching routines -- Henry Spencer
X   regexp.h	-- pattern matching routines -- Henry Spencer
X   regmagic.h	-- pattern matching routines -- Henry Spencer
X   regsub.c	-- pattern matching routines -- Henry Spencer
X   fwin.c	-- utility that returns subsets of files
X   fio.c	-- simple system calls submitted by keni@oasys.dt.navy.mil
X   strftime.c	-- UC Berkeley public domain version of this function
X   Makefile	-- to build your own server
X
X HOMEDIR/src/ansi:
X   *.h		-- specific ANSI string definitions for various programs
X
X HOMEDIR/src/nonansi:
X   *.h		-- specific non-ANSI string definitions for various programs
X
X HOMEDIR/gateway:
X   gateway.c	-- gateway for connecting your host to another on Internet
X   gateway.h	-- specific definitions for gateway.c
X   iulp.h	-- definition of the interactive UNIX ListServer Protocol
X   README	-- how to install and use the gateway
X   Makefile	-- how to build the gateway
X
X HOMEDIR/util:
X   combine.c	-- user utility to automatically combine archive files
X		   received in multiple mail messages
X   expn.c	-- check if address is reachable
X   check_addr	-- script to check the validity of an incoming address
X   mmdfcatmail.c -- utility to use before catmail if using an MMDF mailer
X   which.sh	-- UCB "which" source
X   use_From:_address.sh -- program+script to use the From: address instead of
X			   the "From " for identification
X
X HOMEDIR:
X   makefile	-- to build your system
X   IULP		-- description of the IUL protocol
X   setup        -- a script to be run before starting for the first time
X   systest	-- script that analyzes your system
X   queued	-- daemon controlling the processing of the mail queue
X   peer		-- script to add a peer list
X   news		-- script to add a news group to a list
X   redux        -- script that reduces the size of mbox by removing unnecessary
X                   fields from the header of each message
X   ulock        -- in conjunction with the 'flocks' file, this utility
X                   removes all locked files (unlocks them)
X   flocks       -- script to remove locked files
X   .awk		-- awk program used for the 'statistics' and 'recipients'
X		   ListServer requests
X   .stats	-- shell script used for the 'statistics' ListServer request
X   .grep	-- shell program used for the 'statistics' ListServer request
X   .ignored	-- a list of email addresses whose messages are ignored
X   owners	-- a list of all list owner addresses
X   unwanted.hosts -- hosts not allowed to connect to IUL
X   upgrade_to_6.0 -- upgrade script
X   welcome.live	-- welcome message upon live log in
X   README	-- general guidelines for first time users
X   FAQ		-- most frequently asked questions
X   WHATS_NEW	-- what's with this version
X   config	-- the system's configuration file
X
X
X               |-->-----------> START
X               |                  |
X               |                  |
X               ^          (spawns-and-dies)
X               |                  |
X               |                  |
X               |--<--restart--- SERVERD <--shutdown-<--|
X                                  /\                   r
X                                 /  \                  e
X                                /    \                 s
X                    (spawns either one as necessary)   t       
X                              /        \               a
X                             /          \              r
X                           LIST        LISTSERV --->---t
X
X
XThe diagram shows that 'start' spawns 'serverd' and then dies, and that
X'listserv' may request 'serverd' to die (shutdown) or request that the
Xsystem is restarted, in which case 'serverd' spawns 'start' and dies.
X
XEnjoy!
X
XRevision history:
X
XVersion      Date     Status    Comments
X------------------------------------------------------------------------------
X 3.45      12/20/90  Outdated * First public version; bugs with listserv
X 3.67      01/03/91  Outdated   Bugs fixed
X 3.68      01/04/91  Outdated * v3.67 + tlock utility
X 4.0       04/09/91  Exprmntl   v3.68 + STATISTICS listserv command
X 4.1       04/16/91  Outdated   v4.0 + redux utility
X 4.2       05/02/91  Outdated   v4.1 w/ optimized source code, better doc.
X 4.21      05/03/91  Outdated   v4.2 w/ redux which was left out by mistake
X 4.3       05/03/91  Outdated * v4.21 w/ better mailer-daemon msg handling
X 4.4       05/22/91  Exprmntl   v4.3 w/ enhanced tlock,control,start,listserv
X 4.5	   06/12/91  Exprmntl   v4.3 w/ enhanced listserv
X 5.0	   07/17/91  Outdated * v4.4 + support for multiple lists
X 5.1	   08/27/91  Outdated   v5.0 + bug fixes, enhanced listserv
X 5.1 Rev I 10/03/91  Outdated   v5.0 + more bug fixes
X 5.2	   10/08/91  Outdated * v5.1 + archives, GET, INDEX requests,
X			 	       farch utility, moderated lists,
X				       disabled listserv commands on a per list
X				       basis, link with peer lists and news
X				       feeds
X 5.2 Rev A 10/10/91  Outdated   v5.2 + bug fixes
X 5.21      10/15/91  Exprmntl   v5.2A + multiple recipients in one message
X 5.3 beta  10/31/91  Exprmntl   v5.21 + system mailmethod, bug fixes
X 5.3 	   12/05/91  Outdated   v5.21 + bug fixes, universal system mailmethod,
X					multiple recipients in one message,
X					support for blanks in email addresses,
X					user-set limit on size of messages,
X					RFC 821 compliant SMTP implementation,
X					extensive mail loop detection mechanism,
X					new scripts (stds, reformat), enhanced
X					GET request, and more.
X 5.31	   12/09/91  PstdNews * v5.3 + mail queueing capability/subsystem +
X				       RELEASE request
X 5.4 beta  12/16/91  Exprmntl   v5.31 + crash recovery mechanism, manager
X					approved subscriptions (private lists),
X					multiple lines of text for describing
X					archived files, batch processing,
X					WHICH request, automatic use of
X					unproto in the Makefile, private
X					archives, list owners with restricted
X					system privileges, email aliasing
X					for requests, SYSTEM/REPORTS/PUT/EDIT
X					list administration requests, automatic
X					archiving of distributed messages only,
X					enhanced INDEX/GET requests, same
X					archive names allowed on different
X					levels of the hierarchy (path spec. to
X					an archive)
X 5.4	   02/05/92  Outdated   v5.31 + v5.4 beta
X 5.41	   03/05/92  PstdNews * v5.4 + catmail (system is now more secure), bug
X					fixes, owner preferences
X 5.5 beta  04/08/92  Exprmntl   v5.41 + two ways for moderating lists by owners
X					(+ APPROVE/DISCARD list administration
X					requests), enhanced farch, fix to GET
X					request, list digests, optional
X					Comment: line, manager preferences,
X					enhanced help system, GET/INDEX 
X					requests now report file sizes, new
X					config options ignore_invalid_requests
X					and relaxed_syntax, EXECUTE request
X 5.5	   05/18/92  Outdated   v5.5 beta + NeXT port
X 6.0beta   01/19/93  Exprmntl	v5.5 +	interactive system, ULISTSERVER_UMASK
X					env var, Precedence:, configurable
X					headers, regular expressions, auto
X					archiving of lists' messages, enhanced
X					farch, ability to execute UNIX commands
X					on a per list basis, ability to
X					continue commands in the config file on
X					multiple lines, HP, Apollo, Sequent &
X					DG ports, interception of requests sent
X					to a list, list defaults, CONCEAL
X					attribute, user settable subscription
X					addresses, various user utilities,
X					gateway client, screening and analysis
X					of error messages, support for syslog,
X					auto splitting of outgoing files, FAX
X					request
X
X 6.0	   06/01/93  Released * v6.0beta + ports + bug fixes + SEARCH request
X
X*Source for these versions has been posted to news.
*-*-END-of-src/README-*-*
echo x - src/REGISTRATION
sed 's/^X//' >src/REGISTRATION <<'*-*-END-of-src/REGISTRATION-*-*'
X
X		  	UNIX LISTSERVER SYSTEM
X		  	----------------------
X
X			  Copy Registration
X
X			   tasos@cs.bu.edu
X
XKeep an eye on the anonymous ftp site cs.bu.edu (128.197.10.1) for any
Xupdates, in the directory pub/listserv
X
XThe list unix-listserv@stormking.com served by listserv@stormking.com
Xis a forum about this system.
X
XIf you use this software and have not filled out a registration form before,
Xplease email me the following information:
X
XName:
XEmail address:
XOrganization:
XAddress:
XPhone:
XType of computer system:
XVersion of server using (% list -v):
XComments:
*-*-END-of-src/REGISTRATION-*-*
echo x - src/Makefile
sed 's/^X//' >src/Makefile <<'*-*-END-of-src/Makefile-*-*'
X# Makefile for UNIX ListServer 6.0
X#
X# The programs are written to be fully ANSI C compliant. The symbol CC below
X# may have to be redefined if an ANSI C compiler is to be used. This ANSI
X# compiler should define the marco __STDC__
X#
X# Suggestions:
X# SGI: cc -ansi -D_POSIX_SOURCE, or cc -D__STDC__ -D_POSIX_SOURCE
X# SUN: Use /usr/lang/acc when available
X# NeXT: Compile with -D__NeXT__ if the compiler does not define this symbol,
X#  and -I/usr/include/bsd -I/usr/include/bsd/sys
X# HP-UX: cc -Aa -D_HPUX_SOURCE
X# IBM: xlc -D_ALL_SOURCE -qnoro
X# SYSVR4 Systems: link with -lnsl and -lsocket or -lsockdns if you are using
X#   a name server
X# SCO: Compile with -Dsco -I/usr/include/netinet
X# Apollo: Do not compile with -A ansi; if you need to, also compile with
X#   -Dapollo
X# Sequent: gcc -fwritable-strings
X# Data General: gcc -fwritable-strings
X# Stardent/KPC Titan: cc (do not use -43)
X# OSF: cc -std1
X# i386: cc -fwritable-strings
X# i860: cc
X#
X# To use the unproto package during compilation, uncomment the symbols
X# SRC and COMP below, and comment out the default ones.
X#
X# Also read the README file in this directory as well as ../doc/server.nr
X# (the PORT SPECIFIC section)
X#
X# On some systems that do not use BSD-style sockets and signals (SIGIO)
X# you need to link with a BSD library that defines them, although this is not
X# critical. See the man page for iul(1) for more details.
X#
X# Examples:
X# AIX: Link with -lbsd
X# SCO: Link with -lsocket
X#
X# SIGNALS: Various UNIX versions deal with signals in a number of ways.
X# If your UNIX is strict SYSVR4 compile with -Dsvr4 -- SYSVR4 uses sighold()
X# and sigrelse(); if it is SYSVR3 compile with -Dsvr3 -- the same functions are
X# defined; if it is strict BSD based compile with -Dbsd -- BSD uses
X# sigblock() and sigsetmask(). If your system does not define either of these
X# sets of routines or is not a strict implementation of the above, do not
X# use the above flags and certain signals will be ignored at certain times.
X# In defs.h certain ports are given predefined flavors.
X#
X# LINKING: If you are using the interactive part of ListServer and your
X# setup uses a name server, you may wish to link with libraries that provide
X# DNS support.
X#
X# Optional compiler flags that can be used:
X#   -DHAVE_SELECT_H: if your system provides the <sys/select.h> include file
X#   -DHAVE_ULIMIT_H: if your system provides the <ulimit.h> include file
X#   -DHAVE_SETJMP_H: if your system provides the <setjmp.h> include file
X#   -DHAVE_TZFILE_H: if your system provides the <tzfile.h> include file
X#   -DSYSLOG=facility: if you want to use syslog(3) for reports
X#   -DNO_LOCKS: if you do not want file locking
X#   -DNO_ERRORS_TO: if you do not want Errors-To: lines in messages
X#   -DERROR_MAIL_ANALYSIS=level: if you want the system to analyze 
X#	mailer-daemon error messages and take automatic action; default is
X#	off; level ranges from 1 (very conservative) to 9 (complete analysis +
X#	guess-attempts); see also GRACE_PERIOD below -- this option is still
X#	under development
X#   -DZMAILER: if you are using Zmailer or any other non-sendmail mailer that
X#	requires 'HELO hostname' to start the SMTP transaction; see also below
X#   -DLIST_CHECKING_FOR_REQUESTS: if you want to screen out requests sent to
X#	lists but allow ones that do not refer to local or remote lists
X#   -DLIST_ALIAS_IN_SUBJECT: if you want each subject line to contain the
X#	list's alias and the current message number
X#   -DNEED_DATE: if you need a Date: header line and your sendmail does not
X#	put one
X#   -DWAIT3_NEEDS_UNION: if using wait3() and its first argument is a union
X#   -DNEED_VSPRINTF: if your system does not provide vsprintf()
X#   -DMAX_LISTS=lists+1: number of lists defined plus one; default is 11
X#   -DUCB_MAIL=\"path\": path to UCB mail program; default is /usr/ucb/mail
X#   -DMAX_LINE=size: internal character buffer length; default is 1024
X#   -DMAX_CONNECTIONS=xxx: define the maximum number of live connections over
X#	TCP/IP; default is 5
X#   -DMAX_EMAILS=num: define the number of messages to send before sleeping
X#	to give time to sendmail to catch up; default is 10
X#   -DIUL_PORT=port: define another port for live connections if you do not
X#	have access to /etc/services
X#   -DINEWS=\"path\": path to the inews program for posting articles to news
X#	groups; default is /usr/lib/news/inews
X#   -Dbsd: if your host has a BSD flavor; default is defined in defs.h
X#   -Dsvr3: if your host has a SYSV R3 flavor; default is defined in defs.h
X#   -Dsvr4: if your host has a SYSV R4 flavor; default is defined in defs.h
X#   -Dunknown_port: if the system has not been ported to your host; should
X#	use one of the above system types (bsd, svr3, svr4)
X#   -fwritable-strings: specific to gcc; must be used with gcc
X#
X# Symbols automatically defined for various hosts (depending on the host):
X#   TCP_IP: undefine it in defs.h if you do not have TCP/IP installed
X#   GO_INTERACTIVE: defined if TCP_IP and SIGCLD (SIGCHLD), SIGUSR1 and SIGUSR2
X#	are defined; requires host support for either waitpid() or wait3(), and
X#	semaphores
X#   NO_LOCKS: defined when a system exhibits locking problems over NFS
X#   NONBLOCKING_IO: undefine it if your host does not support it
X#
X# Other symbols that may need tweaking:
X#   HOSTNAME: if TCP_IP is not defined then you need to set your hostname in
X#	defs.h only if you are compiling with -DZMAILER; default is "localhost"
X#   LOCAL_ADDR: same as above; default is 127.0.0.1
X#   SENDMAIL_HOST: default is "localhost"; it defines the host to connect to
X#	when using the 'system' mailmethod to deliver mail (defined in
X#	sysmail.h); compile as follows: -DSENDMAIL_HOST=\"hostname\".
X#   MAILER_DAEMON: email addresses that are given special treatment (defined
X#	in defs.h)
X#   SUSP_SUBJECT: suspicious Subject: lines that get special treatment (defined
X#	in defs.h)
X#   GRACE_PERIOD: time period before subscriptions of erroneous addresses are
X#	suspended; to be used as follows: -DGRACE_PERIOD=time_in_seconds.
X#
X# Always run 'setup' (in the parent directory); do not type 'make' in this
X# directory.
X
X# define your compiler:
XCC		= cc
X
X# define special symbols (system, preprocessor options, etc):
XDEFINES		= -DHAVE_SELECT_H -DHAVE_ULIMIT_H -DHAVE_SETJMP_H \
X		  -DHAVE_TZFILE_H
X
X# define the optimization level
XOPTIMIZATION	= -g
X
X# define where the preprocessor is:
XCPP		= /lib/cpp
X
X# unproto specific definitions -- use either COMP symbol (second is preferred):
X#SRC		= /tmp/$<
X#COMP		= cat $< | unproto > $(SRC); $(CC)
X#COMP		= $(CPP) $(DEFINES) $< | unproto > $(SRC); $(CC)
X
X# default definitions (comment out if using unproto):
XSRC		= $<
XCOMP		= $(CC)
X
X# Reset HOMEDIR to the top level directory the system is installed under:
XHOMEDIR		= /usr/server
X
X# define linking options and libraries
XLDFLAGS		= $(OPTIMIZATION)
XLIBS		=
X
X# Nothing to change below
XSHELL		= /bin/sh
XLD		= $(CC)
XCFLAGS		= -c $(OPTIMIZATION) -I$(PWD) -I$(PWD)/src -I/usr/server/src \
X		  -I/usr/include -I$(HOMEDIR)/src $(DEFINES)
XCLEAN_TMP	= @if [ $$USE_UNPROTO ]; then \
X		    if [ `dirname $(SRC)` = "/tmp" ]; then \
X		      rm -f $(SRC); \
X		    fi; \
X		  else \
X		    echo > /dev/null; \
X		  fi
X
XARCHIVED        = signals.o sender.o misc.o sysmail.o sem.o siul.o regex.o \
X		  regsub.o regerror.o regexp.o fio.o strftime.o
XARCHIVE_LIB     = libserver.a
XTARGETS         = listserv list serverd start tlock farch pqueue catmail iul \
X		  rev fwin
X
Xall: $(ARCHIVE_LIB) $(TARGETS)
X
X$(ARCHIVE_LIB): $(ARCHIVED)
X	@rm -f $(ARCHIVE_LIB)
X	ar cr $(ARCHIVE_LIB) $(ARCHIVED)
X	@if [ -f /usr/bin/ranlib -o -f /bin/ranlib ]; then \
X	  echo ranlib $(ARCHIVE_LIB); \
X	  ranlib $(ARCHIVE_LIB);\
X	else\
X	  echo ar ts $(ARCHIVE_LIB); \
X	  ar ts $(ARCHIVE_LIB); \
X	fi
X
Xlistserv: listserv.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS) -lm
X
Xlist: list.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS) -lm
X
Xserverd: serverd.o $(ARCHIVE_LIB)
X	@rm -f $@
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS) -lm
X
Xstart: start.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS)
X
Xtlock: tlock.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS)
X
Xfarch: farch.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS) -lm
X
Xpqueue: pqueue.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS)
X
Xcatmail: catmail.o $(ARCHIVE_LIB)
X	$(LD) $(LDFLAGS) $@.o $(ARCHIVE_LIB) -o $@ $(LIBS)
X	@chmod a+rx $@
X	@chmod u+s $@
X
Xiul:	iul.o
X	$(LD) $(LDFLAGS) $@.o -o $@ $(LIBS) -lm
X	@chmod a+rx $@
X
Xrev:	rev.o
X	$(LD) $(LDFLAGS) $@.o -o $@
X
Xfwin:	fwin.o
X	$(LD) $(LDFLAGS) $@.o -o $@ $(LIBS) -lm
X
Xinstall: $(ARCHIVE_LIB) $(TARGETS)
X	@for t in $(TARGETS); do \
X	   echo Installing "$$t"; \
X	   rm -f $(HOMEDIR)/"$$t"; \
X	   if [ -r /bin/ln -o -r /usr/bin/ln -o -r /usr/ucb/ln -o \
X	        -r /usr/bsd/ln ]; then \
X	     ln -s $(HOMEDIR)/src/"$$t" $(HOMEDIR)/"$$t"; \
X	   else \
X	     cp "$$t" $(HOMEDIR); \
X	   fi; \
X	   if [ "`ls -l $$t | awk '{ print $$3 }'`" = "server" ]; then \
X	     chmod a+rx $(HOMEDIR)/"$$t"; \
X	   fi; \
X	done
X	@(cd $(HOMEDIR); chmod u+s catmail)
X
Xclean:
X	rm -f $(TARGETS) $(ARCHIVE_LIB) *.o
X
X# Hand-generated dependencies
Xlistserv.o:     listserv.c defs.h struct.h global.h listserv.h ansi/listserv.h \
X                nonansi/listserv.h ansi/misc.h nonansi/misc.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xlist.o:         list.c defs.h struct.h global.h list.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xserverd.o:      serverd.c defs.h struct.h global.h serverd.h ansi/serverd.h \
X                nonansi/serverd.h iulp.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xstart.o:        start.c defs.h struct.h global.h start.h ansi/start.h \
X		nonansi/start.h ansi/misc.h nonansi/misc.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xsender.o:       sender.c defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xsignals.o:      signals.c defs.h struct.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xsysmail.o:      sysmail.c defs.h struct.h sysmail.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xmisc.o:         misc.c defs.h struct.h ansi/misc.h nonansi/misc.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xtlock.o:        tlock.c defs.h global.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xfarch.o:        farch.c defs.h struct.h listserv.h ansi/defs.h nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xpqueue.o:       pqueue.c defs.h struct.h global.h pqueue.h ansi/defs.h \
X		nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xcatmail.o:	catmail.c defs.h struct.h global.h catmail.h ansi/catmail.h\
X		nonansi/catmail.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xsem.o:		sem.c defs.h ansi/defs.h nonansi/defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xiul.o:		iul.c iul.h iulp.h defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xsiul.o:		siul.c iul.h iulp.h defs.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xregex.o:	regex.c regexp.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xregsub.o:	regsub.c regexp.h regmagic.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xregexp.o:	regexp.c regexp.h regmagic.h
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xregerror.o:	regerror.c
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xrev.o:		rev.c
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xfwin.o:		fwin.c
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xfio.o:		fio.c
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
X
Xstrftime.o:	strftime.c
X	@PWD=`pwd`
X	$(COMP) $(CFLAGS) $(SRC)
X	@$(CLEAN_TMP)
*-*-END-of-src/Makefile-*-*
echo x - src/catmail.h
sed 's/^X//' >src/catmail.h <<'*-*-END-of-src/catmail.h-*-*'
X/*
X  Below are the #define's pertinent to catmail.c (user contributed application).
X*/
X
X#ifdef __STDC__
X# include "ansi/catmail.h"
X#else
X# include "nonansi/catmail.h"
X#endif
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
X#define LOST_MAIL		"lost+found"
X
Xint	lfd = 2, lfd2 = 2, lfd3 = 2; /* Set to stderr in case they are closed */
X				     /* without having been opened before */
Xint	listid;
XBOOLEAN tty_echo        = TRUE;
XBOOLEAN moderated       = FALSE;        /* -m flag off */
XBOOLEAN reformat        = FALSE;        /* -f flag off */
XBOOLEAN requests        = FALSE;        /* -r flag off */
XFILE    *report         = NULL;
*-*-END-of-src/catmail.h-*-*
echo x - src/defs.h
sed 's/^X//' >src/defs.h <<'*-*-END-of-src/defs.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement. 
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  General system definitions.
X
X  WARNING: DO NOT CHANGE THE NAME OF THE 'SERVERD', 'LIST' or 'LISTSERV'
X  PROGRAMS in the #define's below. If you do so, make sure that command line
X  options are separated by at least a space from the filename.
X  DO NOT INCLUDE ANY CHARACTERS SUCH AS &, |, <, >. etc.
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __STDC__
X# include "ansi/defs.h"
X#else
X# include "nonansi/defs.h"
X#endif
X
X#define TCP_IP		  /* Remove it if your host does not support it */
X
X#if defined (TCP_IP) && (defined (SIGCLD) || defined (SIGCHLD)) && \
X  defined (SIGUSR1) && defined (SIGUSR2)
X# ifndef GO_INTERACTIVE
X#  define GO_INTERACTIVE 
X   /* Remove this definition if your host doesn't support semaphores etc. */
X# endif
X#endif
X
X/* If you do not have TCP/IP and are using Zmailer, provide the following
X   information about your host.
X*/
X#ifndef TCP_IP
X# define HOSTNAME	  "localhost" /* Replace w/ your host's name */
X# define LOCAL_ADDR	  "127.0.0.1" /* Replace w/ your host's IP address */
X#endif
X
X#define VERSION		  "6.0 -- UNIX ListServer by Anastasios Kotsikonas"
X#define COPYRIGHT	  \
X"UNIX ListServer version 6.0 by Anastasios Kotsikonas, Copyright (c) 1991-93.\n\
XUse, duplication or disclosure by the U.S. Federal Government is subject to the\n\
Xrestrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,\n\
Xfor U.S. Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).\n\n"
X#define SERVICE		  "ulistserv"
X#define COMPLETE_FILE(f)  if (!interactive) fprintf (f, ".\nQUIT\n")
X#define TELNET		  "telnet `hostname` 25 > /dev/null 2>&1 "
X#ifndef INEWS
X# define INEWS		  "/usr/lib/news/inews"
X#endif
X#ifdef xenix
X# define BINMAIL	  "/usr/bin/mail > /dev/null 2>&1"
X#else
X# define BINMAIL	  "/bin/mail > /dev/null 2>&1"
X#endif
X
X#define SUBSCRIBERS       ".subscribers"
X#define ALIASES		  ".aliases"
X#define NEWSF		  ".news"
X#define PEERS		  ".peers"
X#define HEADERS		  ".headers"
X#define RESTRICTED        ".restricted"
X#define IGNORED           ".ignored"
X#define INFO_FILE         ".info"
X#define WELCOME_FILE      ".welcome"
X#define LIST_LIMITS	  ".limits"
X#define UNPROC_MESSAGES	  ".un.messages"
X#define UNPROC_SUBSCRIBERS ".un.subscriber"
X#define UNPROC_PEERS	  ".un.peers"
X#define UNPROC_NEWS	  ".un.news"
X#define UNPROC_DIGEST     ".un.digest" /* digest ready to send */
X#define DIGEST_TOC        ".digest.toc" /* digest table of contents */
X#define DIGEST_MSG        ".digest.msg" /* digest of messages */
X#define DIGEST_TIME       ".digest.time" /* time last digest sent */
X#define REPORT_LIST       ".report.list"
X#define REPORT_LIST_ACC   ".rep.list.acc"
X#define LIST_MAIL_FILE    "mail"
X#define LIST_MODERATED_F  "moderated"
X
X#define MESSAGE_IDS_F     ".message.ids"
X
X#define INDEX		  "INDEX"
X#define DIRF		  "DIR"
X#define DEFAULT_ARCHIVE	  "listserver"
X
X#define START_OPTIONS     "-crs"         /* provide start options here */
X#ifndef apollo
X# define CUT		  "cut"
X#else
X# define CUT		  "/sys5.3/usr/bin/cut"
X#endif
X#define AWK               "awk" /* Do a 'which awk' for proper path */
X#ifdef _AIX
X# define UPTIME		  "ruptime"
X#else
X# define UPTIME            "uptime"  /* 'which uptime'; may use ruptime */
X#endif
X#define MAILER_DAEMON     "MAILER|<DA?EMON&~DEMON\\.CO\\.UK>|POST.*MAST|\
X^ROOT| WPUSER|MMDF|^SMT.*|\\$EMD|MRGATE|VMMAIL|MAIL.*SYSTEM|UUCP|-MAISER-|\
X^MAL@|MAIL.+AGENT|TCPMAIL|BITMAIL|MAILMAN|MAIL_SYSTEM"
X			  /* Preserve upper case; put all possibilities above
X			     separated by a '|'; all of the above entries will
X			     be used to identify mailer daemon messages so
X			     they can get special treatment; entries are
X			     regular expressions */
X#define SUSP_SUBJECT	  \
X"DELIVERY[ \t]+ERROR|\
XDELIVERY[ \t]+REPORT|\
XDELIVERY[ \t]+PROBLEM|\
XUSER[ \t]+UNKNOWN|\
XUNDELIVER.+[ \t]MAIL|\
XUNDELIVER.+[ \t]MESSAGE|\
XPROBLEMS.+DELIVERING.+MAIL|\
XPROBLEMS.+DELIVERING.+MESSAGE|\
XCAN[ \t]*['NO]*T.+DELIVER.+MAIL|\
XUNABLE.+DELIVER.+MAIL|\
XUNABLE.+DELIVER.+MESSAGE|\
XFAILED[ \t]+MAIL|\
XFAILED[ \t]+MESSAGE|\
XMAIL[ \t]+FAILED|\
XMAIL[ \t]+RETURNED|\
XRETURNED[ \t]+MAIL|\
XMAIL.*[ \t]ERROR|\
XMAIL[ \t]+RECEIVED|\
XMESSAGE[ \t]+RECEIVED|\
XREAD[ \t]+RECEIPT|\
XRECEIPT[ \t]+NOTIFICATION|\
XSTATUS.+SIGNAL[ \t]+[0-9]+|\
X^ERROR[ \t]+CONDITION[ \t]+RE:|\
XAUTO[ \t]+REPLY|\
XAUTOMATIC[ \t]+REPLY|\
XAUTOMATICALLY[ \t]+GENERATED|\
XWAITING.+MAIL|\
XON[ \t]+VACATION|\
XCONCIERGE[ \t]+NOTICE|\
XAWAY[ \t]+FROM.+MAIL|\
XCAN[ \t]*['NO]*T.+ANSWER|\
XCAN[ \t]*['NO]*T.+REPLY|\
XOUT[ \t]+OF[ \t]+TOWN|\
X^[ \t]*TEST[ \t]*$|\
X^[ \t]*TEST.+IGNORE|\
XPLEASE[ \t]+IGNORE"
X			  /* List of suspicious subject lines; possible mail
X			     loop */
X#ifndef UCB_MAIL
X# define UCB_MAIL	  "/usr/ucb/mail" /* Path to UCB mail program. Redefine
X			  it if UCB mail is not installed on your system */
X#endif
X
X/*
X  These #define's should not be altered.
X*/
X
X#ifndef NSIG
X# ifdef MAXSIG
X#  define NSIG		  MAXSIG
X# else
X#  define NSIG            32
X# endif
X#endif
X
X#define DEFAULT_IUL_PORT  372	/* As assigned by the IANA */
X#define MAX_COMMANDS      27	/* # of commands recognized */
X#ifndef MAX_EMAILS
X#define MAX_EMAILS	  10	/* ListServer emails in one outgoing batch */
X#endif
X#define MAX_FILE_LENGTH   500	/* Number of lines of files when shrunk */
X#define BUFFSIZ		  8192	/* Socket buffer size */
X#define DEFAULT_SERVER_ADDRESS 	  "listserver"
X#define DEFAULT_SERVER_CMDOPTIONS ""
X#define DEFAULT_SERVER_COMMENT	  "Boston University ListServer"
X#define DEFAULT_MANAGER		  "server"
X#define DEFAULT_PRECEDENCE	  "bulk"
X#ifndef MAX_LINE
X# define MAX_LINE	  1024
X#endif
X#define EOS		  '\0'
X#define RESET(var)	  (var[0]) = EOS
X#define BSD_PS		  0x01
X#define	SYSV_PS		  0x02
X#define	USE_TELNET	  0x04
X#define USE_ENV_VAR	  0x08
X#define USE_MY_SYSTEM	  0x10
X#define BSD_MAIL	  0x20
X#define POST_MAIL	  0x40
X#define GATE_MAIL	  0x80
X#define USE_SYSMAIL	  0x100
X#define LIMIT_MSG	  0x200
X#define LIMIT_FILES	  0x400
X#define IGNR_INVLD_RQSTS  0x4000
X#define RELAXED_SYNTAX	  0x8000
X#define NON_AUTO_SUB	  0x10000
X#define CONCEAL_LIST	  0x20000
X#define ARCHIVE_LIST	  0x40000
X#define ARCHIVE_DIGEST	  0x80000
X#define START_OF_MESSAGE  "From " /* UNIX mail messages start w/ this string */
X#define ACK		  "ACK"   /* Send message back to sender */
X#define NOACK		  "NOACK" /* Do not send message back to sender */
X#define POSTPONE	  "POSTPONE"  /* Postpone sending mail */
X#define DIGEST            "DIGEST" /* send mail in digests */
X#define MAX_SET_OPTIONS   4
X
X#define MAX_OWNER_PREFS   15
X#define CCSET		  "CCSET"
X#define	CCSUB		  "CCSUBSCRIBE" /* Copy owner on subscribe request */
X#define CCUNSUB		  "CCUNSUBSCRIBE" /* Copy owner on unsubscribe req */
X#define CCREC		  "CCRECIPIENTS"
X#define CCINFO		  "CCINFORMATION"
X#define CCSTAT		  "CCSTATISTICS"
X#define CCGET		  "CCGET"
X#define CCINDEX		  "CCINDEX"
X#define CCLISTS		  "CCLISTS"
X#define CCRELEASE	  "CCRELEASE"
X#define CCHELP		  "CCHELP"
X#define CCPRIVATE	  "CCPRIVATE"
X#define CCRUN		  "CCRUN"
X#define CCERRORS	  "CCERRORS" /* Copy owner on errors */
X#define CCALL		  "CCALL" /* Copy onwer on all activities */
X
X#define ccset		  0x01
X#define ccsub		  0x02
X#define ccunsub		  0x04
X#define ccrec		  0x08
X#define ccinfo		  0x10
X#define ccstat		  0x20
X#define ccget		  0x40
X#define ccindex		  0x80
X#define cclists		  0x100
X#define ccrelease	  0x200
X#define cchelp		  0x400
X#define ccprivate	  0x800
X#define ccrun		  0x1000
X#define ccerrors	  0x2000
X#define ccall		  ccset | ccsub | ccunsub | ccrec | ccinfo | ccstat |\
X			  ccget | ccindex | cclists | ccrelease | cchelp |\
X			  ccprivate | ccrun | ccerrors
X
X#define MAX_SIGNAL	  NSIG    /* Highest system signal caught */
X#define BOOLEAN		  unsigned int
X#ifndef TRUE
X# define TRUE		  1
X#endif
X#ifndef FALSE
X# define FALSE		  0
X#endif
X#define CANT_OPEN	  (-1)
X#define CANT_LOCK	  (-2)
X#define SUBSCRIBED	  TRUE    /* Subscribed sender */
X#define NOTSUBSCRIBED	  FALSE   /* Sender is not subscribed */
X#define NEWS		  TRUE+1  /* News feed */
X#define PEER		  TRUE+2  /* Peer messages */
X#define OWNER		  TRUE+3  /* Sender is a list owner */
X#define MANAGER		  TRUE+4  /* Sender is the manager */
X#define NEW_ARRIVAL	  "\n--- NEW MAIL HAS ARRIVED ---\n"
X#define PROCESSING_BATCH  "\n--- PROCESSING BATCH REQUESTS ---\n"
X#define DIGEST_TIME_	  "\n--- DIGEST TIME ---\n"
X#ifndef MAX_LISTS
X# define MAX_LISTS	  11 /* It should be one more than # desired */
X#endif
X
X#define RECEIVED	  "^(Received:[ \t])"
X#define FROM		  "^(From:[ \t])"
X#define SUBJECT		  "^(Subject:[ \t])"
X#define MESSAGE_ID1       "^(Message-Id:[ \t])"
X#define MESSAGE_ID2	  "^(Message-ID:[ \t])"
X#define MESSAGE_ID3	  "^(Message-id:[ \t])"
X#define MESSAGE_TAG	  "^(Message-Tag:[ \t])"
X#define ERROR_CONDITION	  "Error Condition Re: "
X
X#define COMPLETE_TELNET(f) \
X if (sys.options & USE_TELNET) \
X   COMPLETE_FILE (f)
X
X#define OPEN_FILE(fp, file, mode, func) \
X  if ((fp = fopen (file, mode)) == NULL)\
X    report_progress (report, tsprintf ("\n%s(): Could not open %s: errno %d",\
X				       func, file, errno), TRUE),\
X    gexit (1)
X
X#define COPY_OWNER(mask) (BOOLEAN)\
X((listid < 0 ? sys.server.manager_prefs : sys.lists[listid].owner_prefs) &\
X (mask))
X
X#define PREPEND(str, buf) \
X  { char copy [1024];\
X    RESET (copy);\
X    strcat (copy, buf);\
X    sprintf (buf, "%s%s", str, copy);\
X  }
X
X/*
X  System specific definitions:
X*/
X
X#if defined (sequent) || defined (unknown_port)
X# ifndef SEEK_SET
X#  define SEEK_SET	0
X# endif
X# ifndef SEEK_CUR
X#  define SEEK_CUR	1
X# endif
X#endif
X
X#if defined (_MINIX) || defined (ultrix) || defined (__NeXT__) || \
X  defined (apollo) || defined (i386)
X# ifndef NO_LOCKS
X#  define NO_LOCKS
X# endif
X# if !defined (bsd) && !defined (svr3) && !defined (svr4)
X#  define bsd
X# endif
X# ifndef ultrix
X#  undef GO_INTERACTIVE
X# endif
X#endif
X
X#if defined (__convex__)
X# ifdef __STDC__
X#  ifndef NO_LOCKS
X#   define NO_LOCKS
X#  endif
X#  ifdef TCP_IP
X#   undef TCP_IP
X#  endif
X# endif
X# ifdef GO_INTERACTIVE
X#  undef GO_INTERACTIVE
X# endif
X#endif
X
X#if defined (sun) || defined (hpux) || defined (__hpux) || \
X  defined (sequent) || defined (ultrix) || defined (__convex__) || \
X  defined (__NeXT__)
X# if !defined (bsd) && !defined (svr3) && !defined (svr4)
X#  define bsd
X# endif
X#endif
X
X#if defined (sgi) || defined (__sgi) || defined (mips) || defined (__mips) ||\
X  defined (_AIX) || defined (stellar) || defined (titan) || defined (xenix) ||\
X  defined (sco) || defined (M_UNIX) || defined (M_XENIX) || defined (__osf__)
X# if !defined (svr3) && !defined (svr4) && !defined (bsd)
X#  define svr3
X# endif
X# ifdef xenix
X#  ifndef NO_LOCKS
X#   define NO_LOCKS
X#  endif
X# endif
X# if defined (titan) || defined (stellar)
X#  ifdef GO_INTERACTIVE
X#   undef GO_INTERACTIVE
X#  endif
X# endif
X#endif
X
X#if defined (i860) || defined (stardent) || defined (__DGUX__)
X# if !defined (bsd) && !defined (svr3) && !defined (svr4)
X#  define svr4
X# endif
X#endif
X
X#define NONBLOCKING_IO
X
X/*
X These functions don't have prototypes in SCO UNIX in the included files.
X
Xextern void exit (int);
Xextern int unlink (char *);
Xextern int stat (char *, struct stat *);
Xextern int mkdir (char *, int);
Xextern int lockf (int, int, int);
Xextern int open (char *, int, ...);
Xextern int creat (char *, int);
Xextern int close (int);
Xextern int getpid ();
Xextern int stat (char *, struct stat *);
Xextern int chmod (char *, int);
Xextern int chdir (char *);
Xextern int unlink (char *);
Xextern unsigned int sleep (unsigned int);
Xextern int execl (char *, ...);
Xextern int read (int, void *, unsigned);
Xextern int write (int, void *, unsigned);
Xextern int umask (int);
Xextern char *getenv (char *);
Xextern void free (void *);
Xextern int kill (int, int);
Xextern int getuid (void);
Xextern int abort (void);
Xextern int access (char *, int);
Xextern void *calloc (int, int);
X*/
*-*-END-of-src/defs.h-*-*
echo x - src/global.h
sed 's/^X//' >src/global.h <<'*-*-END-of-src/global.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
Xchar subscribersf [MAX_LINE];
Xchar newsf [MAX_LINE];
Xchar peersf [MAX_LINE];
Xchar aliasesf [MAX_LINE];
Xchar headersf [MAX_LINE];
Xchar restrictedf [MAX_LINE];
Xchar ignoredf [MAX_LINE];
Xchar list_mail_f [MAX_LINE];
Xchar list_moderated_f [MAX_LINE];
Xchar report_listf [MAX_LINE];
Xchar server_ignoredf [MAX_LINE];
Xchar infof [MAX_LINE];
Xchar welcomef [MAX_LINE];
Xchar unprocessed_messages [MAX_LINE];
Xchar unprocessed_subscribersf [MAX_LINE];
Xchar unprocessed_peersf [MAX_LINE];
Xchar unprocessed_newsf [MAX_LINE];
Xchar unprocessed_digestf [MAX_LINE];
Xchar message_idsf [MAX_LINE];
Xchar message_id [MAX_LINE];
Xchar digest_msgf [MAX_LINE];
Xchar digest_timef [MAX_LINE];
Xchar password_in_sub_file [MAX_LINE];
Xchar **alternate_addresses;
Xchar *prog;
XSYS  sys;
XCOMMANDS commands [MAX_COMMANDS]; /* Set of recognizable commands */
XREMOTE *rlists = NULL, *matched_rlists = NULL;
XBOOLEAN debug = FALSE;
X
X/*
X  Below are the valid SET options, their valid values and their
X  default values.
X*/
X
X/* valid SET options */
Xchar *options [] = { "ADDRESS", "MAIL", "PASSWORD", "CONCEAL" };
X
X/* and values */
Xchar *values [] =
X{ "^FIXED$|^VARIABLE$", "^ACK$|^NOACK$|^POSTPONE$|^DIGEST$", ".*", "^YES$|^NO$" };
X
Xchar *default_values [] = { "FIXED", "NOACK", ".*", "NO" };
X
*-*-END-of-src/global.h-*-*
echo x - src/iul.h
sed 's/^X//' >src/iul.h <<'*-*-END-of-src/iul.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to iul.c and siul.c
X*/
X
X#include "iulp.h"
X
X#define PORT            	372	/* As assigned by IANA */
X#define MAX_HOPS		5	/* ... from IUL server to IUL server */
X#define TIMEOUT         	180 /* How long to wait for server response */
X#ifndef BUFFSIZ
X# define BUFFSIZ         	8192
X#endif
X#ifndef RESET
X# define RESET(v)        	(v[0] = EOS)
X#endif
X#define PRINTF(code, str)	if (verbose) printf ("%d %s", code, str),\
X				fflush (stdout)
X#ifndef MIN
X# define MIN(a, b)		((a) < (b) ? (a) : (b))
X#endif
X
X#define GENERAL_RESPONSES(cmd)\
X{\
X  switch (cmd) {\
X    case OK: PRINTF (OK, "OK\n"); break;\
X    case CONNECT: PRINTF (CONNECT, "Connected\n"); break;\
X    case SYNTAX_ERROR: PRINTF (SYNTAX_ERROR, "Syntax error in request\n");\
X	 break;\
X    case INVALID_REQ: PRINTF (INVALID_REQ, "Invalid request\n"); break;\
X    case PEER_UNAVAIL: PRINTF (PEER_UNAVAIL, "Peer unavailable\n"); break;\
X    case BAD_ARCHIVE: PRINTF (BAD_ARCHIVE, "Bad archive\n"); break;\
X    case RESTRICTED_REQ: PRINTF (RESTRICTED_REQ,\
X	 "Restriction in force for request\n"); break;\
X    case NOT_OWNER: PRINTF (NOT_OWNER, "Invalid onwer\n"); break;\
X    case SYS_ERROR: PRINTF (SYS_ERROR, "System error\n"); break;\
X    case MESSAGE: PRINTF (MESSAGE, "Message\n"); break;\
X    case PERMISSION_DENIED: PRINTF (PERMISSION_DENIED, "Permission denied\n");\
X	 break;\
X    case CONN_CLOSED: PRINTF (CONN_CLOSED, "Closing connection\n"); break;\
X    case CONN_ABORTED: PRINTF (CONN_ABORTED, "Connection aborted\n");\
X	 return -1; break;\
X    case CONN_TIMEOUT: PRINTF (CONN_TIMEOUT, "Connection timed out\n");\
X	 return -1; break;\
X    case SERVER_BUSY: PRINTF (SERVER_BUSY, "Server busy\n"); break;\
X    case PASSWORD_REQUIRED: PRINTF (PASSWORD_REQUIRED, "Password required\n");\
X         if (read_from_fd (sock_fd, nbytes, NULL) < 0) return -1; break;\
X    case CONTINUED: PRINTF (CONTINUED, "[Command incomplete:]"); break;\
X    case MORE_INPUT_REQUIRED: PRINTF (MORE_INPUT_REQUIRED,\
X	 "[Command incomplete:]"); break;\
X  }\
X}
*-*-END-of-src/iul.h-*-*
echo x - src/iulp.h
sed 's/^X//' >src/iulp.h <<'*-*-END-of-src/iulp.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Define the Interactive UNIX ListServer Protocol.
X*/
X
X#define OK                      100
X#define MORE_INPUT_REQUIRED	101
X#define CONTINUED		102
X#define PASSWORD_REQUIRED	103
X#define CONNECT			105
X#define MESSAGE			106
X#define WRITE_TO_FILE_ASC	110
X#define WRITE_TO_FILE_BIN	120
X#define APPEND_TO_FILE_ASC	130
X#define APPEND_TO_FILE_BIN	140
X#define TEST_FILE_PERMISSIONS	154
X#define SYNTAX_ERROR            200
X#define INVALID_REQ             300
X#define PEER_UNAVAIL            400
X#define BAD_ARCHIVE             500
X#define RESTRICTED_REQ          600
X#define NOT_OWNER               700
X#define SYS_ERROR               800
X#define PERMISSION_DENIED	860
X#define SERVER_BUSY             870
X#define CONN_TIMEOUT		880
X#define CONN_ABORTED		890
X#define CONN_CLOSED		900
*-*-END-of-src/iulp.h-*-*
echo x - src/list.h
sed 's/^X//' >src/list.h <<'*-*-END-of-src/list.h-*-*'
X/* 
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to list.c
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
X#ifndef GRACE_PERIOD
X# define GRACE_PERIOD	  604800  /* Time in secs after which offending */
X				  /* addresses are removed/suspended */
X#endif
X#define ONE_MONTH	  2592000 /* 30 days: error messages in the database */
X				  /* older than this are removed */
X#define INEWS_REPLY	  "/tmp/.inews.reply"
X#define ERRORSF		  ".errors"
X#define ERRORS2F	  ".errors.tmp"
X#define UNPROC_TMP	  ".un.tmp"
X#define MAIL_COPY	  ".messages"
X#define MSG               ".msg"
X#undef MAILFORWARD
X#define MAILFORWARD       ".mailforward"
X#define HEADER		  ".header"
X#define MSG_NO            ".msgno"
X#define DIGEST_NO         ".digestno"
X#define REMOVED_USERS	  "removed.users"
X#define REMOVED_ALIASES	  "removed.alias"
X#define MBOX              "mbox"            /* Place to save list's messages */
X#define ARCHIVE		  "archive"	    /* Public messages */
X#define ORIGIN		  "^(Originator:[ \t])"
X#define SENDER            "^(Sender:[ \t])"
X#define REPLY_TO	  "^(Reply-To:[ \t])"
X#define DATE              "^(Date:[ \t])"
X#define ARCHIVE_NAME	  "^(archive-name:[ \t])"
X#define CONTROL		  "^(Control:[ \t])"
X#define NO_RECIPIENT_FILE "NONE"
X#define MESSAGE_SEPARATOR \
X"----------------------- Message requiring your approval ----------------------"
X#define REQUESTS \
X"(^[ \t]*APPR?O?V?E?[ \t]+([A-Z0-9.@-]+)[ \t]+.+[ \t]+[0-9]+[ \t]*$)|\
X(^[ \t]*DISC?A?R?D?[ \t]+([A-Z0-9.@-]+)[ \t]+.+[ \t]+[0-9]+[ \t]*$)|\
X(^[ \t]*EDIT[ \t]+([A-Z0-9.@-]+)[ \t]+.+[ \t]+[A-Z]+[ \t]*$)|\
X(^[ \t]*GET[ \t]+[A-Z0-9/.-]+.+)|\
X(^[ \t]*HELP[ \t]*[A-Z0-9]*$)|\
X(^[ \t]*INDEX[ \t]*[A-Z0-9/.-]*.*)|\
X(^[ \t]*INFO?R?M?A?T?I?O?N?[ \t]+([A-Z0-9.@-]+)[ \t]*$)|\
X(^[ \t]*LIST?S?[ \t]*$)|\
X(^[ \t]*PUT[ \t]+([A-Z0-9.@-]+)[ \t]+.+)|\
X(^[ \t]*RECI?P?I?E?N?T?S?[ \t]+([A-Z0-9.@-]+)[ \t]*$)|\
X(^[ \t]*REVI?E?W?[ \t]+([A-Z0-9.@-]+)[ \t]*$)|\
X(^[ \t]*RELE?A?S?E?[ \t]*$)|\
X(^[ \t]*REPO?R?T?S?[ \t]+([A-Z0-9.@-]+)[ \t]+.+$)|\
X(^[ \t]*RUN[ \t]+([A-Z0-9.@-]+))|\
X(^[ \t]*SET[ \t]+([A-Z0-9.@-]+))|\
X(^[ \t]*STAT?I?S?T?I?C?S?[ \t]+([A-Z0-9.@-]+))|\
X(^[ \t]*SUBS?C?R?I?B?E?[ \t]+([A-Z0-9.@-]+).+)|\
X(^[ \t]*SYST?E?M?[ \t]+([A-Z0-9.@-]+)[ \t]+.+[ \t]+#.+)|\
X(^[ \t]*UNSU?B?S?C?R?I?B?E?[ \t]*([A-Z0-9.@-]*))|\
X(^[ \t]*SIGN?O?F?F?[ \t]*([A-Z0-9.@-]*))|\
X(^[ \t]*PLEASE[ \t]+REMOVE[ \t]+)|\
X(^[ \t]*PLEASE[ \t]+UNSU?B?S?C?R?I?B?E?[ \t]+)|\
X(^[ \t]*PLEASE[ \t]+SIGN[ \t]+)|\
X(^[ \t]*PLEASE[ \t]+ADD[ \t]+)|\
X(^[ \t]*WHIC?H?[ \t]*$)|\
X(^[ \t]*SHUTDOWN[ \t]+.+$)|\
X(^[ \t]*RESTART[ \t]+.+$)|\
X(^[ \t]*EXEC?U?T?E?[ \t]+.+#.+)"
X
X#define APPEND_TELNET(func) \
X  if ((sys.options & USE_TELNET)) {\
X    if ((f = fopen (mailforwardf, "a")) == NULL)\
X      report_progress (report, tsprintf ("%s(): Could not open %s", func, \
X		       mailforwardf), TRUE),\
X      gexit (1);\
X    COMPLETE_FILE (f);\
X    fclose (f);\
X  }
X
X#define DELIVER_MAIL(recipient) \
X  if (!interactive) {\
X    if ((++mails_sent) >= MAX_EMAILS)\
X      mails_sent = 0,\
X      sleep (30);\
X    if (sys.options & USE_SYSMAIL) \
X      sysmail (mailforwardf); \
X    else \
X      syscom ("%s '%s' < %s", sys.mail.method, \
X              (((sys.options & USE_TELNET) == 0) ? locase (recipient) : ""), \
X              mailforwardf);\
X  }
X
X#define NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS(__address__, __func__, from_sender) \
X  { char error [MAX_LINE];\
X    report_progress (report, tsprintf ("Syntax error in user address: %s\n%s",\
X __address__, (from_sender ? "Message not delivered to any recipients" : \
X"Message not delivered to this recipient")), TRUE);\
X    create_header (&f, mailforwardf, sys.server.address, sys.server.address,\
X		   sys.manager, "Syntax error in user address", TRUE, FALSE,\
X		   TRUE);\
X    fprintf (f, "Error detected in user address: %s\n", __address__);\
X    if (from_sender)\
X      fprintf (f, "Message not distributed to list %s.\n",\
Xsys.lists[listid].alias);\
X    else\
X      fprintf (f, "Message not delivered to this recipient.\n");\
X    fprintf (f, "The message is included below:\n\
X-----------------------------------------------------------------------------\n\
X");\
X    fclose (f);\
X    cat_append (headerf, mailforwardf);\
X    cat_append (msgf, mailforwardf);\
X    APPEND_TELNET (__func__);\
X    DELIVER_MAIL (sys.manager);\
X  }
X
X#define REMOVE_ADDRESS(address, from, to, append_to) \
X{\
X  FILE *fin, *fout;\
X  char line[MAX_LINE], linecopy[MAX_LINE], *error;\
X  long int len, written;\
X  OPEN_FILE (fin, from, "r", "process_message");\
X  OPEN_FILE (fout, to, "w", "process_message");\
X  while (!feof (fin)) {\
X    RESET (line);\
X    fgets (line, MAX_LINE - 2, fin);\
X    strcpy (linecopy, line);\
X    upcase (linecopy);\
X    if (line[0] != EOS) {\
X      if (linecopy[strlen (linecopy) - 1] == '\n')\
X	linecopy[strlen (linecopy) - 1] = EOS;\
X      if (removed || re_strcmp (address, linecopy, NULL) <= 0) {\
X	if ((written = write_to_fd (fileno (fout), line, (len = strlen (line))))\
X	    < 0) {\
X	  echo_append ((error = tsprintf ("write() failed; wrote %ld bytes \
Xout of %ld requested to file %s; errno %d", abs (written), len, to, errno)), WARNING);\
X	  if (sys.options & BSD_MAIL)\
X	    syscom ("echo '%s' | %s -s 'write() failed; system shut down' %s",\
X		    error, UCB_MAIL, sys.manager);\
X	  exit (16);\
X	}\
X      }\
X      else {\
X        if (line[strlen (line) - 1] == '\n')\
X	  line[strlen (line) - 1] = EOS;\
X	echo_append (line, append_to);\
X        removed = TRUE;\
X      }\
X    }\
X  }\
X  fclose (fin);\
X  fclose (fout);\
X}
X
X#define SUSPEND_ADDRESS(address, from, to) \
X{\
X  FILE *fin, *fout;\
X  char line [MAX_LINE], linecopy [MAX_LINE], addr [MAX_LINE], *error;\
X  long int len, written;\
X  OPEN_FILE (fin, from, "r", "process_message");\
X  OPEN_FILE (fout, to, "w", "process_message");\
X  while (!feof (fin)) {\
X    RESET (line);\
X    fgets (line, MAX_LINE - 2, fin);\
X    strcpy (linecopy, line);\
X    upcase (linecopy);\
X    if (line[0] != EOS) {\
X      if (linecopy[strlen (linecopy) - 1] == '\n')\
X	linecopy[strlen (linecopy) - 1] = EOS;\
X      if (suspended || re_strcmp (address, linecopy, NULL) <= 0) {\
X	if ((written = write_to_fd (fileno (fout), line, (len = strlen (line))))\
X	    < 0) {\
X	  echo_append ((error = tsprintf ("write() failed; wrote %ld bytes \
Xout of %ld requested to file %s; errno %d", abs (written), len, to, errno)), WARNING);\
X	  if (sys.options & BSD_MAIL)\
X	    syscom ("echo '%s' | %s -s 'write() failed; system shut down' %s",\
X		    error, UCB_MAIL, sys.manager);\
X	  exit (16);\
X	}\
X      }\
X      else\
X	sscanf (line, "%s", addr),\
X	suspended = TRUE,\
X	fprintf (fout, "%s %s %s", addr, POSTPONE, skip_to_word (line, 3));\
X    }\
X  }\
X  fclose (fin);\
X  fclose (fout);\
X}
X
Xchar digest_tocf [MAX_LINE];
Xchar mboxf [MAX_LINE];
Xchar msg_nof [MAX_LINE];
Xchar digest_nof [MAX_LINE];
Xchar msgf [MAX_LINE];
Xchar mailforwardf [MAX_LINE];
Xchar archivef [MAX_LINE];
Xchar headerf [MAX_LINE];
Xchar mail_copyf [MAX_LINE];
Xchar unprocessed_tmp [MAX_LINE];
Xchar removed_usersf [MAX_LINE];
Xchar removed_aliasesf [MAX_LINE];
Xchar archive_name [MAX_LINE];
Xchar limitsf [MAX_LINE];
Xchar errorsf [MAX_LINE];
Xchar errors2f [MAX_LINE];
Xchar **multi_recipients;  /* List of multiple recipient addresses */
Xchar *list_alias  = NULL; /* arg to the -L command option */
Xchar *one_digest  = NULL; /* arg to the -i command option */
Xchar *mask;
Xchar hostname [256];
X
XFILE *mail        = NULL; /* Source of messages */
XFILE *report      = NULL; /* Progress report to the administrator */
XFILE *subscribers = NULL; /* List of subscribers */
XFILE *news	  = NULL; /* List of newsgroups */
XFILE *peers	  = NULL; /* List of peers */
XFILE *restricted  = NULL; /* List of people whose messages require special
X                              handling */
XFILE *ignored     = NULL; /* List of people whose messages are ignored */
XFILE *msg_no      = NULL; /* Last message count */
XFILE *digest_no   = NULL; /* Last digest count */
XFILE *headers     = NULL; /* File containing headers of messages only */
XFILE *message_ids = NULL; /* File containing message ids */
X
XBOOLEAN tty_echo	    = FALSE; /* -e option off */
XBOOLEAN send_to_subscribers = TRUE;  /* -r option off */
XBOOLEAN execute_once        = FALSE; /* -1 option off */
XBOOLEAN errors_to_owner	    = FALSE; /* -f option off */
XBOOLEAN multi_recip	    = FALSE; /* -m option off */
XBOOLEAN force_digest	    = FALSE; /* -d option off */
XBOOLEAN is_moderated	    = FALSE; /* -M option off */
XBOOLEAN article_replies_to_author  = FALSE; /* -p option off */
XBOOLEAN message_replies_to_author  = FALSE; /* -P option off */
XBOOLEAN do_not_check_subscriptions = FALSE; /* -s option off */
XBOOLEAN append_to_digest;	     /* gets set if anyone wants digests */
XBOOLEAN message_rejected    = FALSE;
XBOOLEAN interactive	    = FALSE;
X
Xint     returned_msg	    = 0;     /* Counts the invalid messages */
Xint     public_msg	    = 0;     /* Counts the public messages */
Xint     digest_msg	    = 0;     /* Counts the digest messages */
Xint	mails_sent	    = 0;     /* Counts email sent */
Xint     listid		    = -1;
Xint	maxrecipients	    = 0;
Xint	lfd		    = 2;
Xint	nlists		    = 0;
X
Xtypedef struct _matched_prec_header {
X  char	 *line;
X  struct _matched_prec_header *next;
X} MATCHED_PREC_HEADER;
X
XMATCHED_PREC_HEADER *matched_prec_header;
X
Xchar *mname[] = {
X  "jan", "feb", "mar", "apr", "may", "jun",
X  "jul", "aug", "sep", "oct", "nov", "dec",
X};
X
X/* The following warnings assume that the user address follows the SMTP
X   code where () are present -- not safe with messages from BITNET mailers.
X   list.c will have to be altered. Code still under testing. */
X
Xchar *warnings =	/* MAILER_DAEMON warnings */
X"^421 ([^ \t]+)|\
X^450 ([^ \t]+)|\
X^451 ([^ \t]+)|\
X^452 ([^ \t]+)|\
X^501 ([^ \t]+)|\
X^552 ([^ \t]+)|\
X^554 NO[ \t]+SUCH[ \t]+USER:?([^ \t]+)&~HOST[ \t]+UNKNOWN.+AUTHORITATIVE.+ANSWER.+FROM.+NAME.+SERVER|\
X^554 ([^ \t]+)&~HOST[ \t]+UNKNOWN.+AUTHORITATIVE.+ANSWER.+FROM.+NAME.+SERVER|\
XCONNECTION[ \t]+TIMED|\
XSERVICE[ \t]+UNAVAILABLE|\
XNETWORK[ \t]+IS[ \t]+UNREACHABLE|\
XUNKNOWN[ \t]+MAILER[ \t]+ERROR|\
XCONNECTION[ \t]+RESET|\
XREMOTE[ \t]+PROTOCOL[ \t]+ERROR|\
XTIMEOUT.+WAITING[ \t]+FOR[ \t]+INPUT|\
XPERMISSION[ \t]+DENIED|\
XCONNECTION[ \t]+ABORTED|\
XCONNECTION[ \t]+REFUSED|\
XQUOTA[ \t]+EXCEEDED|\
XDISK[ \t]+FULL|\
XUNKNOWN[ \t]+DOMAIN|\
XUNABLE.+SEND.+TO|\
XHOST[ \t]+UNKNOWN&~AUTHORITATIVE.+ANSWER.+FROM.+NAME.+SERVER|\
XNEVER.+HEARD.+OF.+HOST&~AUTHORITATIVE.+ANSWER.+FROM.+NAME.+SERVER|\
XUNABLE[ \t]+TO[ \t]+SEND[ \t]+MAIL";
X
Xchar *errors =		/* MAILER_DAEMON serious error messages */
X"UNKNOWN[ \t]+USER[ \t]+([^ \t]+)[ \t]+AT[ \t]+NODE[ \t]+([^ \t]+)|\
XNO[ \t]+SUCH[ \t]+USER[ \t]+([^ \t]+)[ \t]+AT[ \t]+NODE[ \t]+([^ \t]+)|\
XUNKNOWN[ \t]+USER[ \t]+([^ \t]+)[ \t]+AT[ \t]+SITE[ \t]+([^ \t]+)|\
XNO[ \t]+SUCH[ \t]+USER[ \t]+([^ \t]+)[ \t]+AT[ \t]+SITE[ \t]+([^ \t]+)|\
XUNKNOWN[ \t]+USER|\
XUSER[ \t]+UNKNOWN|\
XHOST[ \t]+UNKNOWN.+AUTHORITATIVE.+ANSWER.+FROM.+NAME.+SERVER|\
X^550 ";
X
Xchar *fatal_subjects = 	/* Serious error messages in Subject: lines */
X"CAN.*T.+SEND.+FOR.+WEEK|\
XCAN.*T.+SEND.+FOR[ \t]+([^ \t]+)[ \t]+DAYS";
*-*-END-of-src/list.h-*-*
echo x - src/listserv.h
sed 's/^X//' >src/listserv.h <<'*-*-END-of-src/listserv.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to listserv.c
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __STDC__
X# include "ansi/listserv.h"
X#else
X# include "nonansi/listserv.h"
X#endif
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
X#define UPDATE_DATE	  "June 1, 1993"
X#define RECIP_FILE        ".recip"
X#define USERS_FILE        "/tmp/users"
X#define PEER_SERVER_REQUEST "^(Peer Server Request:[ \t])"
X#define START_OF_SIGNATURE "--"
X#define ALIASES_TIMESTAMPF ".time.aliases"
X#define IGNORED_TIMESTAMPF ".time.ignored"
X#define INFO_TIMESTAMPF    ".time.info"
X#define SUBSCRIBERS_TIMESTAMPF ".time.subscrib"
X#define WELCOME_TIMESTAMPF ".time.welcome"
X#define NEWS_TIMESTAMPF    ".time.news"
X#define PEERS_TIMESTAMPF   ".time.peers"
X
X#define DELIVER_MAIL(recipient, copy_owner) \
X{\
X  if (fax_it) \
X    syscom ("%s < %s %s > /dev/null &", sys.fax.prog,\
X	    MAILFORWARD, sys.fax.fax_no);\
X  else if (!interactive) {\
X    if ((++mails_sent) >= MAX_EMAILS)\
X      mails_sent = 0,\
X      sleep (30);\
X    if (sys.options & USE_SYSMAIL) \
X      sysmail (MAILFORWARD); \
X    else \
X      syscom ("%s '%s' '%s' < %s", sys.mail.method, \
X              (((sys.options & USE_TELNET) == 0) ? locase (recipient) : ""), \
X	      ((copy_owner) ? \
X	       ((listid >= 0 ? sys.lists[listid].owner : sys.manager)) \
X	       : ""), MAILFORWARD);\
X  }\
X}
X
X#define APPEND_TELNET(func) \
X  if (!interactive && !fax_it && (sys.options & USE_TELNET)) {\
X    if ((f = fopen (MAILFORWARD, "a")) == NULL)\
X      report_progress (report, tsprintf ("%s(): Could not open %s", func, \
X					 MAILFORWARD), TRUE),\
X      gexit (1);\
X    COMPLETE_FILE (f);\
X    fclose (f);\
X  }
X
X#define NOTIFY_MANAGER(msg) \
X{\
X  create_header (&f, MAILFORWARD, sys.server.address, sys.manager, sender,\
X		 FALSE, OK, TRUE, FALSE);\
X  fprintf (f, "%s %s\n", msg, sender);\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sys.manager, FALSE);\
X  report_progress (report, tsprintf ("%s %s; forwarding message to %s\n", msg,\
Xsender, sys.manager), FALSE);\
X}
X
X#define NOTIFY_OF_BAD_ARCHIVE(msg, archive, reply_code) \
X{\
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,\
X		 COPY_OWNER (ccerrors), reply_code, FALSE, TRUE);\
X  fprintf (f, msg, archive);\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sender, COPY_OWNER (ccerrors));\
X}
X
X#define NOTIFY_OF_REQUEST_FORWARDING \
X{\
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,\
X		 OK, FALSE, FALSE);\
X  if (interactive) {\
X    int ret;\
X    REMOTE *r = matched_rlists;\
X    fclose (f);\
X    while (r) {\
X      if (r->inet_addr[0] != EOS) {\
X	f = fopen (MAILFORWARD, "a");\
X        fprintf (f, "*** %s: Contacting UNIX ListServer %s @ %s, port %d ...\n",\
X		 hostname, r->listserv, r->inet_addr, r->port);\
X        fclose (f);\
X        if ((ret = siul (r->inet_addr, r->port, sender, "", 60, MAILFORWARD,\
X		         request)) < 0)\
X	  reply_code (SYS_ERROR),\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Local system error. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X	else if (ret == PEER_UNAVAIL)\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Peer unavailable. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X	else if (ret == CONN_ABORTED)\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Connection aborted. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X	else if (ret == CONN_TIMEOUT)\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Connection timed out. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X	else if (ret == SERVER_BUSY)\
X	  reply_code (SERVER_BUSY),\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Remote server busy. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X	else if (ret == SYS_ERROR)\
X	  reply_code (SYS_ERROR),\
X	  f = fopen (MAILFORWARD, "a"),\
X	  fprintf (f, "### %s: Remote system error. Please send email.\n",\
X		   hostname),\
X	  fclose (f);\
X      }\
X      else\
X	f = fopen (MAILFORWARD, "a"),\
X	fprintf (f, "??? %s: UNIX ListServer %s cannot be contacted; please \
Xsend email.\n", hostname, r->listserv),\
X	fclose (f);\
X      r = r->next;\
X    }\
X    return;\
X  }\
X  fprintf (f, "List %s is not local. Your request is forwarded to the \
Xfollowing list\nserver(s):\n\n", sys.lists[nlists].alias);\
X  {\
X    REMOTE *r = matched_rlists;\
X    while (r)\
X      fprintf (f, "%s (List address: %s)\n", r->listserv, r->address),\
X      r = r->next;\
X  }\
X  fprintf (f, "\nYou may wish to send any such future requests to this/these \
Xserver(s).\n");\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sender, FALSE);\
X}
X
X#define FORWARD_REQUEST \
X  {\
X    REMOTE *r = matched_rlists;\
X    while (r) {\
X      create_header (&f, MAILFORWARD, sender, r->listserv, "", FALSE, OK, \
X		     FALSE, FALSE);\
X      fprintf (f, "%s\n", request);\
X      COMPLETE_TELNET (f);\
X      fclose (f);\
X      DELIVER_MAIL (r->listserv, FALSE);\
X      r = r->next;\
X    }\
X  }
X
X#define MEMBERS_ONLY \
X{\
X  BOOLEAN ok_to_reset_address = FALSE;\
X  if ((sys.lists[listid].defaults.set_values[0][0] == EOS &&\
X       !strcmp (default_values [0], "VARIABLE")) ||\
X      (!strcmp (sys.lists[listid].defaults.set_values[0], "VARIABLE")))\
X    ok_to_reset_address = TRUE;\
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,\
X		 COPY_OWNER (ccprivate), RESTRICTED_REQ, FALSE, FALSE);\
X  fprintf (f, "%s: List %s is private for members only.\nOnly subscribers may \
Xissue this request.\n", sender, sys.lists[listid].alias);\
X  if (alternate_addresses) {\
X    fprintf (f, "\nIn addition, the system found the following \
Xaddress(es) that resemble yours.\nIf one of these is you, please resend your \
Xmessage from that one%s:\n\n",\
X	     (ok_to_reset_address ? \
X	      ", or use the\n'set <list> address' request to change the \
Xaddress you are subscribed with" :\
X	      ""));\
X    {\
X      int i;\
X      for (i = 0; alternate_addresses[i]; ++i)\
X	fprintf (f, "%s\n", alternate_addresses[i]),\
X	free ((char *) alternate_addresses[i]);\
X      free ((char **) alternate_addresses);\
X      alternate_addresses = NULL;\
X      fprintf (f, "\n");\
X    }\
X  }\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sender, COPY_OWNER (ccprivate));\
X}
X
X#define NOT_LIST_OWNER \
X{\
X  reject_mail (sender, request, tsprintf ("%s: You are not the owner of this \
Xlist\n", sender), NOT_OWNER, 0);\
X  if (!interactive) {\
X    create_header (&f, MAILFORWARD, sys.server.address,sys.lists[listid].owner,\
X		   "WARNING: Hacker attack", FALSE, NOT_OWNER, TRUE, FALSE);\
X    fprintf (f, "The following request was sent to this ListServer by %s:\n\n%s\
X\nThe password provided appears in the report file\n",\
X	     sender, request);\
X    COMPLETE_TELNET (f);\
X    fclose (f);\
X    DELIVER_MAIL (sys.lists[listid].owner, FALSE);\
X  }\
X}
X
X#define NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS(__address__) \
X{\
X  report_progress (report, "Syntax error in user address\n", FALSE);\
X  create_header (&f, MAILFORWARD, sys.server.address, sys.manager,\
X                 "Syntax error in user address", FALSE, SYNTAX_ERROR, TRUE,\
X		 TRUE);\
X  if (request [0] != EOS)\
X    fprintf (f, ">%s\n", request);\
X  fprintf (f, "Error detected in user address: %s\nNo requests processed.\n",\
X __address__);\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sys.manager, FALSE);\
X}
X
X#define NO_SUCH_MESSAGE_TAG(_tag_)\
X{\
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,\
X		 SYNTAX_ERROR, FALSE, TRUE);\
X  fprintf (f, "No message found with tag number %d.\n", _tag_);\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sender, FALSE);\
X}
X
X#define CANNOT_LOCK_FILE(_file_)\
X{\
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,\
X		 SYS_ERROR, FALSE, TRUE);\
X  fprintf (f, "Cannot lock file %s.\nYour request has to be resubmitted.\n",\
X_file_);\
X  COMPLETE_TELNET (f);\
X  fclose (f);\
X  DELIVER_MAIL (sender, FALSE);\
X}
X
X#define COMMAND(index, _name_, _mask_, _func_)\
X  commands[index].name = _name_;\
X  commands[index].mask = _mask_;\
X  commands[index].func = (FUNC) _func_
X
X#define PUT_TIMESTAMP(_targetfile_, _stampfile_) \
X  stat (_targetfile_, &stat_buf);\
X  echo (tsprintf ("%d", stat_buf.st_mtime), _stampfile_)
X
X#define COPY_MESSAGE \
X{\
X  char *error;\
X  long int len, written;\
X  sign[0] = RESET (line);\
X  signature = FALSE;\
X  sprintf (sign, "%s\n", START_OF_SIGNATURE);\
X  while (!feof (mail) && /* Copy till eof or next message */\
X         (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {\
X    if (!strcmp (line, sign))\
X      signature = TRUE;\
X    else if (!signature)\
X      if ((written = write_to_fd (fileno (f), line, (len = strlen (line)))) \
X	  < 0) {\
X	echo_append ((error = tsprintf ("write() failed; wrote %ld bytes \
Xout of %ld requested; errno %d", abs (written), len, errno)), WARNING);\
X	if (sys.options & BSD_MAIL)\
X	  syscom ("echo '%s' | %s -s 'write() failed; system shut down' %s",\
X		  error, UCB_MAIL, sys.manager);\
X	exit (16);\
X      }\
X    RESET (line);\
X    fgets (line, MAX_LINE - 2, mail);\
X  }\
X  if (!strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))\
X    fseek (mail, -strlen (line), SEEK_CUR); /* Move back to beginning */\
X}
X
X#define CHECK_TIMESTAMP(_stampfile_, _targetfile_)\
X  if ((f = fopen (_stampfile_, "r")) != NULL) {\
X    fscanf (f, "%d\n", &timestamp);\
X    fclose (f);\
X    stat (_targetfile_, &stat_buf);\
X    if (stat_buf.st_mtime != timestamp) {\
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,\
X		     FALSE, PERMISSION_DENIED, FALSE, TRUE);\
X      fprintf (f, "File %s has been modified since you last\nedited it. The \
Xfile has to be reedited again.\n%s\
XYour PUT request is returned to you unprocessed below:\
X\n\n%s\n", _targetfile_, \
X(!interactive ? \
X"An EDIT request will be forced now and the new file will arrive \
Xin a separate\nmessage.\n\n" : ""), \
Xrequest);\
X      COPY_MESSAGE;\
X      COMPLETE_TELNET (f);\
X      fclose (f);\
X      DELIVER_MAIL (sender, FALSE);\
X      upcase (sender);\
X      if (!interactive) /* No need to force EDIT for a live request */\
X        get_sys_files ("EDIT", params_copy, sender);\
X      return;\
X    }\
X  }
X
X#define REMOVE_ADDRESS(address, from, to) \
X{\
X  FILE *fin, *fout;\
X  long int len, written;\
X  char line[MAX_LINE], linecopy[MAX_LINE], *error;\
X  BOOLEAN removed = FALSE;\
X  OPEN_FILE (fin, from, "r", "unsubscribe");\
X  OPEN_FILE (fout, to, "w", "unsubscribe");\
X  while (!feof (fin)) {\
X    RESET (line);\
X    fgets (line, MAX_LINE - 2, fin);\
X    strcpy (linecopy, line);\
X    upcase (linecopy);\
X    if (line[0] != EOS) {\
X      if (linecopy[strlen (linecopy) - 1] == '\n')\
X	linecopy[strlen (linecopy) - 1] = EOS;\
X      if (removed || re_strcmp (address, linecopy, NULL) <= 0) {\
X	if ((written = write_to_fd (fileno (fout), line, (len = strlen (line))))\
X	    < 0) {\
X	  echo_append ((error = tsprintf ("write() failed; wrote %ld bytes \
Xout of %ld requested to file %s; errno %d", abs (written), len, to, errno)), WARNING);\
X	  if (sys.options & BSD_MAIL)\
X	    syscom ("echo '%s' | %s -s 'write() failed; system shut down' %s",\
X		    error, UCB_MAIL, sys.manager);\
X	  exit (16);\
X	}\
X      }\
X      else\
X	removed = TRUE;\
X    }\
X  }\
X  fclose (fin);\
X  fclose (fout);\
X}
X
X#define THANKS	"THANK|GRACIAS|TODA|MERCI|DANK|ARIGATO|XIE.*XIE|DZENKUJEM|\
XTAK|D'AKUJEM|MAHALO|BEDANKT|GROET|E.[XH]AR[IH]ST[OW]|SINCERELY|TRULY|LATER|\
XSPOCIBO"
X
X
Xchar recipf [MAX_LINE];	  /* path to list + RECIP_FILE */
Xchar requests_file [MAX_LINE];
Xchar original_params [10240];
Xchar aliases_timestampf [MAX_LINE];
Xchar ignored_timestampf [MAX_LINE];
Xchar info_timestampf [MAX_LINE];
Xchar subscribers_timestampf [MAX_LINE];
Xchar welcome_timestampf [MAX_LINE];
Xchar news_timestampf [MAX_LINE];
Xchar peers_timestampf [MAX_LINE];
Xchar psr_string [MAX_LINE];
X
XFILE *mail        = NULL; /* Source of messages */
XFILE *report      = NULL; /* Progress report to the administrator */
XFILE *ignored     = NULL; /* List of people whose messages are ignored */
XFILE *msg_no      = NULL; /* Last message count */
XFILE *message_ids = NULL; /* List of message ids */
X
Xint	 lfd			= 2;
Xint      listid			= -1;
Xint      request_no		= 0;	/* Counts the public messages */
Xint      nlists			= -1;	/* # of lists defined in CONFIG */
Xint	 mails_sent		= 0;	/* Count mail sent */
Xlong int restricted_commands	= 0;	/* Mask of restrictions */
Xlong int disabled_commands	= 0;	/* Mask for disabling commands */
Xlong int batched_commands	= 0;	/* Mask for batching commands */
XBOOLEAN  one_rejection		= FALSE;
XBOOLEAN  restart_sys		= FALSE;
XBOOLEAN  tty_echo		= FALSE;	/* -e option off */
XBOOLEAN  do_not_notify_peer_server = FALSE;
XBOOLEAN  peer_server_request	= FALSE;
XBOOLEAN  process_batch		= FALSE;	/* -B option off */
XBOOLEAN  interactive		= FALSE;	/* -i option off */
XBOOLEAN  fax_it			= FALSE;
Xchar	 hostname [256];
X
X/* Codes for helpful messages when a request is rejected. */
X#define REJECT_LIST	1
X#define REJECT_REQUEST	2
X#define REJECT_NAME	3
X#define REJECT_AT	4
*-*-END-of-src/listserv.h-*-*
echo x - src/next.h
sed 's/^X//' >src/next.h <<'*-*-END-of-src/next.h-*-*'
X/*
X  #define's specific to NeXT hosts.
X*/
X
X#ifndef S_IRWXU
X# define 	S_IRWXU 0000700 /* rwx, owner */
X#endif
X#ifndef S_IRUSR
X# define         S_IRUSR 0000400 /* read permission, owner */
X#endif
X#ifndef S_IWUSR
X# define         S_IWUSR 0000200 /* write permission, owner */
X#endif
X#ifndef S_IXUSR
X# define         S_IXUSR 0000100 /* execute/search permission, owner */
X#endif
X#ifndef S_IRWXG
X# define 	S_IRWXG 0000070 /* rwx, group */
X#endif
X#ifndef S_IRGRP
X# define         S_IRGRP 0000040 /* read permission, group */
X#endif
X#ifndef S_IWGRP
X# define         S_IWGRP 0000020 /* write permission, grougroup */
X#endif
X#ifndef S_IXGRP
X# define         S_IXGRP 0000010 /* execute/search permission, group */
X#endif
X#ifndef S_IRWXO
X# define 	S_IRWXO 0000007 /* rwx, other */
X#endif
X#ifndef S_IROTH
X# define         S_IROTH 0000004 /* read permission, other */
X#endif
X#ifndef S_IWOTH
X# define         S_IWOTH 0000002 /* write permission, other */
X#endif
X#ifndef S_IXOTH
X# define         S_IXOTH 0000001 /* execute/search permission, other */
X#endif
*-*-END-of-src/next.h-*-*
echo x - src/pqueue.h
sed 's/^X//' >src/pqueue.h <<'*-*-END-of-src/pqueue.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to pqueue.c
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
XFILE	*report		= NULL;  /* Progress report */
XBOOLEAN tty_echo	= FALSE; /* -e option off */
Xint	lfd		= 2;
Xint	listid		= -1;    /* Dummy */
*-*-END-of-src/pqueue.h-*-*
echo x - src/regexp.h
sed 's/^X//' >src/regexp.h <<'*-*-END-of-src/regexp.h-*-*'
X/*
X * Definitions etc. for regexp(3) routines.
X *
X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
X * not the System V one.
X */
X#define NSUBEXP  10
Xtypedef struct regexp {
X	char *startp[NSUBEXP];
X	char *endp[NSUBEXP];
X	char regstart;		/* Internal use only. */
X	char reganch;		/* Internal use only. */
X	char *regmust;		/* Internal use only. */
X	int regmlen;		/* Internal use only. */
X	char program[1];	/* Unwarranted chumminess with compiler. */
X} regexp;
X
Xextern regexp *regcomp();
Xextern int regexec();
Xextern void regsub();
Xextern void regerror();
*-*-END-of-src/regexp.h-*-*
echo x - src/regmagic.h
sed 's/^X//' >src/regmagic.h <<'*-*-END-of-src/regmagic.h-*-*'
X/*
X * The first byte of the regexp internal "program" is actually this magic
X * number; the start node begins in the second byte.
X */
X#define	MAGIC	0234
*-*-END-of-src/regmagic.h-*-*
echo x - src/serverd.h
sed 's/^X//' >src/serverd.h <<'*-*-END-of-src/serverd.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to serverd.c
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __STDC__
X# include "ansi/serverd.h"
X#else
X# include "nonansi/serverd.h"
X#endif
X
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
X#define MAX_TRIES	 10		/* wait MAX_TRIES * 30 seconds */
X
XFILE	*report		= NULL;
XBOOLEAN tty_echo	= FALSE;	/* -e option off */
X
Xstruct _list_limits {
X  char list_limitsf [MAX_LINE],
X       list_mail_f [MAX_LINE],
X       digest_msgf [MAX_LINE],
X       digest_timef [MAX_LINE];
X  int  nmessages;
X  long int current_date;
X  BOOLEAN done_for_the_day;
X} lists [MAX_LISTS];
X
Xchar *exit_string[] = {  /* Define exit status strings */
X/* 0 */ "OK", 
X/* 1 */ "Could not open or lock file", 
X/* 2 */ "SIGINT signal",
X/* 3 */ "Command line option error",
X/* 4 */ "Syntax error in file",
X/* 5 */ "Could not spawn",
X/* 6 */ "Shutdown request",
X/* 7 */ "Restart request",
X/* 8 */ "Received system signal",
X/* 9 */ "Too many multiple recipients",
X/* 10 */"Could not deliver mail",
X/* 11 */"Malloc failed",
X/* 12 */"Cannot fork",
X/* 13 */"Socket connection problem",
X/* 14 */"Semaphore error",
X/* 15 */"Cannot setuid, setgid",
X/* 16 */"Internal error",
X/* 17 */"Unknown exit status"
X};
X
X#ifdef GO_INTERACTIVE
XBOOLEAN interactive	= FALSE;	/* -i option off */
X#endif
Xint	lfd	 = 2;
Xint	nlists;
Xint	ppid;
X
X#ifndef WAIT3_NEEDS_UNION
X# if defined (sequent) || defined (stardent) || defined (stellar) || \
X  defined (titan) || defined (unknown_port)
X#  ifdef WEXITSTATUS
X#   undef WEXITSTATUS
X#  endif
X#  ifdef WTERMSIG
X#   undef WTERMSIG
X#  endif
X#  ifdef WIFSIGNALED
X#   undef WIFSIGNALED
X#  endif
X#  ifdef WIFEXITED
X#   undef WIFEXITED
X#  endif
X# endif
X#endif
X
X#ifndef WEXITSTATUS
X# define  WEXITSTATUS(stat)	((int)(((stat)>>8)&0377))
X#endif
X
X#ifdef GO_INTERACTIVE
X# ifndef WTERMSIG
X#  define WTERMSIG(stat)	(((int)((stat)&0377))&0177)
X# endif
X# ifndef WIFSIGNALED
X#  define WIFSIGNALED(stat)	(((int)((stat)&0377))>0&&((int)(((stat)>>8)&\
X				0377))==0)
X# endif
X# ifndef WIFEXITED
X#  define WIFEXITED(stat)	(((int)((stat)&0377))==0)
X# endif
X# include "iulp.h"
X
X# ifndef MAX_CONNECTIONS
X#  define MAX_CONNECTIONS 5
X# endif
X# define TIMEOUT_SIG	SIGUSR1	/* Signal sent when client is preempted */
X# define ABORT_SIG	SIGUSR2 /* Signal sent when server shuts down */
X
X# define LOGIN		"email address [none]: "
X# define PASSWORD	"Password: "
X# define PROMPT		"request> "
X# define CONT_PROMPT	"> "
X
X# define MANAGER_LOGIN \
X"You have logged in with manager privileges; you may not issue 'execute'\n\
Xrequests.\n\n"
X
X# define OWNER_LOGIN \
X"You have logged in with owner privileges; valid requests are:\n\n\
Xhelp [topic]\n\
Xset <list> [<option> <arg[s]>] -\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n\
Xunsubscribe <list> - alias signoff\n\
Xrecipients <list> - alias review\n\
Xinformation <list>\n\
Xstatistics <list> [subscriber email address(es)] [-all]\n\
Xrun <list> [password cmd [args]]\n\
Xlists\n\
Xwhich\n\
Xindex [archive | path-to-archive] [/password] [-all]\n\
Xget <archive | path-to-archive> <file> [/password] [parts]\n\
Xsearch <archive | path-to-archive> [/password] [-all] <pattern>\n\
Xrelease\n\
Xsystem <list> <password> <user-address> #user-request\n\
Xreports <list> <password>\n\
Xedit <list> <password> <file>\n\
X\tfile: subscribers/aliases/news/peers/ignored/info/welcome\n\
Xput <list> <password> <keyword> [args]\n\
X\tkeyword:alias/ignore/subscribers/aliases/news/peers/ignored/info/welcome\n\
X\targs:email address for alias/ignore\n\
Xapprove <list> <password> <tag>\n\
Xdiscard <list> <password> <tag>\n\n\
XYou may replace the administrative <password> with -\n\
XBuilt in commands are: quit/exit, ?/privileges, timeleft, binary, ascii\n\
XRequests may be continued on another line if they are terminated with &\\n\n\
XInput/output may be redirected with <, > and >>.\n\
XUNIX pipes are formed with the \"|\" symbol.\n\
XFor 'fax' requests, please send email.\n\
XUsing BINARY as default transfer mode.\n\n"
X
X# define SUBSCRIBER_LOGIN \
X"You have logged in with subscriber privileges; you may only issue the \
Xfollowing\nrequests:\n\n\
Xhelp [topic]\n\
Xset <list> [<option> <arg[s]>] -\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n\
Xunsubscribe <list> - alias signoff\n\
Xrecipients <list> - alias review\n\
Xinformation <list>\n\
Xstatistics <list> [subscriber email address(es)] [-all]\n\
Xrun <list> [password cmd [args]]\n\
Xlists\n\
Xwhich\n\
Xindex [archive | path-to-archive] [/password] [-all]\n\
Xget <archive | path-to-archive> <file> [/password] [parts]\n\
Xsearch <archive | path-to-archive> [/password] [-all] <pattern>\n\
Xrelease\n\n\
X<current-password> can be replaced with -\n\
XBuilt in commands are: quit/exit, ?/privileges, timeleft, binary, ascii\n\
XRequests may be continued on another line if they are terminated with &\\n\n\
XInput/output may be redirected with <, > and >>.\n\
XUNIX pipes are formed with the \"|\" symbol.\n\
XFor 'fax' requests, please send email.\n\
XUsing BINARY as default transfer mode.\n\n"
X
X# define GENERAL_LOGIN \
X"You have logged in as a casual user; you may only issue the following \
Xrequests:\n\n\
Xhelp [topic]\n\
Xrecipients <list> - alias review\n\
Xinformation <list>\n\
Xstatistics <list> [subscriber email address(es)] [-all]\n\
Xlists\n\
Xindex [archive | path-to-archive] [/password] [-all]\n\
Xget <archive | path-to-archive> <file> [/password] [parts]\n\
Xsearch <archive | path-to-archive> [/password] [-all] <pattern>\n\
Xrelease\n\n\
XBuilt in commands are: quit/exit, ?/privileges, timeleft, binary, ascii\n\
XRequests may be continued on another line if they are terminated with &\\n\n\
XInput/output may be redirected with <, > and >>.\n\
XUNIX pipes are formed with the \"|\" symbol.\n\
XFor 'fax' requests, please send email.\n\
XUsing BINARY as default transfer mode.\n\n"
X
X# define SERVER_BUSY_ \
X"All interactive UNIX ListServer ports are busy. Please try again later.\n"
X
X# define GOOD_BYE \
X"UNIX ListServer closing connection.\n"
X
X# define CONN_TIMED_OUT \
X"Connection timed out.\n"
X
X# define SERVER_SHUTS_DOWN \
X"Server shutting down.\n"
X
X# define REMOTE_SYS_ERROR \
X"Remote system error.\n"
X
X# define MORE_INPUT \
X"[Enter text below, end with a '.' in a line by itself]\n"
X
X# define NULL_REDIRECT \
X"Invalid null output redirect\n"
X
X# define BINARY_XFER \
X"Transfer mode reset to binary\n"
X
X# define ASCII_XFER \
X"Transfer mode reset to ASCII\n"
X
X# define SERVICING_OTHER_CONN \
X"Server busy processing another request; please wait...\n"
X
X# define YOU_ARE_NEXT \
X"Processing your request...\n"
X
X# define CLIENT_LOST(exitcode) \
Xclose (msg_sock),\
X_exit (exitcode)  
X
X# define REPLY(msg_sock, code, bytes)\
X{\
X  sprintf (reply, "%d %d \n", code, (bytes));\
X  if (write_to_fd (msg_sock, reply, strlen (reply)) < 0)\
X    CLIENT_LOST (13);\
X}
X 
X# define REPLY_FILE(msg_sock, code, bytes, file)\
X{\
X  sprintf (reply, "%d %d %s\n", code, (bytes), file);\
X  if (write_to_fd (msg_sock, reply, strlen (reply)) < 0)\
X    CLIENT_LOST (13);\
X}
X
X# define CLOSE_CLIENT(msg_sock) \
X  if (msg_sock >= 0) {\
X    REPLY (msg_sock, CONN_CLOSED, strlen (GOOD_BYE));\
X    write_to_fd (msg_sock, GOOD_BYE, strlen (GOOD_BYE));\
X    close (msg_sock);\
X  }
X
X# define CANNOT_CONNECT(__msg__, __code__, __reply__) \
X{\
X  sprintf (msg, "%d %d %s %d %d\n%d %d \n%s", CONNECT, conn_duration,\
X	   version, strlen (PROMPT), strlen (CONT_PROMPT), __code__,\
X	   strlen (__reply__), __reply__);\
X  write_to_fd (msg_sock, msg, strlen (msg));\
X  close (msg_sock);\
X  report_progress (report, __msg__, TRUE);\
X}
X
X# define GET_LOGIN(buf) \
X{\
X  ch = EOS;\
X  bytes_read = 0;\
X  while (ch != '\n' && bytes_read < MAX_LINE) {\
X    if (read (msg_sock, &ch, 1) <= 0 || ch == EOF)\
X      goto abort;\
X    if (time (0) - time_started > conn_duration)\
X      close_connection (TIMEOUT_SIG);\
X    buf [bytes_read++] = ((ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r')\
X|| (ch == (char) 127) ? ((char) 0) : ch);\
X  }\
X  buf [bytes_read] = EOS;\
X  clean_request (buf);\
X  l = 0;\
X  while (buf [l] != EOS) {\
X    if (buf [l] == '\n' || buf [l] == '\r')\
X      buf [l] = EOS;\
X    ++l;\
X  }\
X}
X
X# ifndef SYSLOG
X#  define IN_CRITICAL_SECTION(__func__, msg_sock) \
X{\
X  char msg [256];\
X  long int time_started, time_is, delay = 0;\
X  time (&time_started);\
X  while (interactive && msg_sock && !semctl (sid, 0, GETVAL)) {\
X    REPLY (msg_sock, MESSAGE, strlen (SERVICING_OTHER_CONN));\
X    write_to_fd (msg_sock, SERVICING_OTHER_CONN,strlen (SERVICING_OTHER_CONN));\
X    --time_started;\
X    while (((time (0) - time_started) % 10) && !semctl (sid, 0, GETVAL));\
X    delay = 1;\
X  }\
X  if (interactive && P (sid) < 0) {\
X    report_progress (report, tsprintf ("\n%s(): Error P()", __func__), TRUE);\
X    fclose (report);\
X    CLOSE_CLIENT (msg_sock);\
X    _exit (14);\
X  }\
X  if (delay) {\
X    REPLY (msg_sock, MESSAGE, strlen (YOU_ARE_NEXT));\
X    write_to_fd (msg_sock, YOU_ARE_NEXT, strlen (YOU_ARE_NEXT));\
X  }\
X}
X# else
X#  define IN_CRITICAL_SECTION(__func__, msg_sock) \
X{\
X  char msg [256];\
X  long int time_started, time_is, delay = 0;\
X  time (&time_started);\
X  while (interactive && msg_sock && !semctl (sid, 0, GETVAL)) {\
X    REPLY (msg_sock, MESSAGE, strlen (SERVICING_OTHER_CONN));\
X    write_to_fd (msg_sock, SERVICING_OTHER_CONN,strlen (SERVICING_OTHER_CONN));\
X    --time_started;\
X    while (((time (0) - time_started) % 10) && !semctl (sid, 0, GETVAL));\
X    delay = 1;\
X  }\
X  if (interactive && P (sid) < 0) {\
X    report_progress (report, tsprintf ("\n%s(): Error P()", __func__), TRUE);\
X    CLOSE_CLIENT (msg_sock);\
X    _exit (14);\
X  }\
X  if (delay) {\
X    REPLY (msg_sock, MESSAGE, strlen (YOU_ARE_NEXT));\
X    write_to_fd (msg_sock, YOU_ARE_NEXT, strlen (YOU_ARE_NEXT));\
X  }\
X}
X# endif
X
X# ifndef SYSLOG
X#  define OUT_OF_CRITICAL_SECTION(__func__) \
X{\
X  char msg [256];\
X  if (interactive && V (sid) < 0) {\
X    report_progress (report, tsprintf ("\n%s(): Error V()", __func__), TRUE);\
X    fclose (report);\
X    CLOSE_CLIENT (msg_sock);\
X    _exit (14);\
X  }\
X}
X# else
X#  define OUT_OF_CRITICAL_SECTION(__func__) \
X{\
X  char msg [256];\
X  if (interactive && V (sid) < 0) {\
X    report_progress (report, tsprintf ("\n%s(): Error V()", __func__), TRUE);\
X    CLOSE_CLIENT (msg_sock);\
X    _exit (14);\
X  }\
X}
X# endif
X
Xtypedef struct {
X  long int pid,			/* Client pid */
X  	   time,		/* Time connection established */
X  	   connid;		/* Client id */
X  char name [MAX_LINE];		/* Remote host's IP address or name */
X} CLIENT;
X
XCLIENT clients [MAX_CONNECTIONS];
X  
Xlong int chpid, nforks, sock_fd, msg_sock;
Xint sid, connid, conn_duration;
Xstruct passwd *pwentry;
Xchar requests_file [MAX_LINE];
X
XBOOLEAN chdied = FALSE;
X#endif
*-*-END-of-src/serverd.h-*-*
echo x - src/start.h
sed 's/^X//' >src/start.h <<'*-*-END-of-src/start.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X
X  Below are the #define's pertinent to start.c
X
X  Preserve any quotes that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#ifdef __STDC__
X# include "ansi/start.h"
X#else
X# include "nonansi/start.h"
X#endif
X
X#ifdef __NeXT__
X# include "next.h"
X#endif
X
X#define MODERATED_MAIL_FILE "moderated"
X
X#define START_ABORT \
X  fprintf (stderr, "### start aborted; system not started. ###\n"),\
X  exit (1);
X
X#define DEFAULT_ALIASES \
X"^@.*:(.*)@(.*) \\1@\\2"
X
Xchar    report_list_accf [MAX_LINE];
Xchar	*pids [] = { PID_SERVERD, PID_LIST, PID_SERVER, PID_PQUEUE, PID_QUEUED,
X		     NULL };
Xchar	*mask;
XBOOLEAN tty_echo = TRUE;
XFILE	*report  = NULL;
*-*-END-of-src/start.h-*-*
echo x - src/struct.h
sed 's/^X//' >src/struct.h <<'*-*-END-of-src/struct.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
Xtypedef struct _prec_header {	/* Header lines to be saved */
X  char   *line;
X  struct _prec_header *next;
X} PRECIOUS_HEADER;
X
Xtypedef struct _unix_cmds {
X  char password [MAX_LINE],	/* Password for this UNIX command */
X       name [MAX_LINE],		/* Name of the UNIX command users may execute */
X       *cmd,			/* Actual UNIX command to execute */
X       *comment;		/* Syntax message, or any other message */
X  struct _unix_cmds *next;	/* Pointer to next structure */
X} UNIX_CMDS;
X
Xtypedef struct {
X  char set_values [MAX_SET_OPTIONS][MAX_LINE];
X} DEFAULTS;
X
Xtypedef struct {
X  struct {
X    char alias [MAX_LINE],	/* As it appears in the aliases file */
X	 comment [MAX_LINE],	/* Comment: line in the outgoing mail message */
X         address [MAX_LINE],	/* Full email address of the list */
X	 owner [MAX_LINE],	/* Email address of the list's owner */
X	 password [MAX_LINE],	/* Password that owner uses */
X         cmdoptions [MAX_LINE],	/* List-specific command line options */
X	 arch_dir [MAX_LINE],	/* Directory to put archive files in */
X	 farch_dir [MAX_LINE],	/*     " in which to record archive in DIR */
X	 arch_spec [MAX_LINE],	/* Specifies name of archive files */
X	 arch_pass [MAX_LINE];	/* Password (if any) for the archive */
X    int	 owner_prefs,		/* Owner preferences */
X	 disabled_commands,	/* Mask of disabled commands for this list */
X	 digest_lines,          /* Max number of lines in digest */
X	 digest_hours,          /* Send out digest every so many hours */
X	 ncmds,			/* Count of UNIX commands that may be executed*/
X	 max_messages,		/* Maximum number of messages per day */
X	 options;		/* Mask of options: autosub, conceal, archive */
X    PRECIOUS_HEADER *header;	/* Precious header lines */
X    UNIX_CMDS *unix_cmds;	/* UNIX commands that users may execute */
X    DEFAULTS defaults;		/* Default list settings */
X  } lists [MAX_LISTS];		/* Structure for each list */
X  struct {
X    char address [MAX_LINE],	/* Full email address of the server */
X	 comment [MAX_LINE],	/* Comment: line in the outgoing mail message */
X         cmdoptions [MAX_LINE],	/* ListServer-specific command line options */
X	 password [MAX_LINE];	/* For 'shutdown', 'restart' and 'execute' */
X    int manager_prefs;		/* System manager preferences */
X  } server;			/* Structure for the server */
X  struct {
X    char *method,		/* Mail method to use */
X	 env_var [MAX_LINE],	/* Environment variable to use */
X	 mail_prog [MAX_LINE],	/* Which mail program to use */
X	 precedence [MAX_LINE];	/* Which mail precedence to use */
X  } mail;			/* Mail method structure */
X  struct {
X    long int msg,		/* Maximum message limit in bytes */
X	     files;		/* Maximum file size before auto splitting */
X  } limits;			/* Various limits are defined here */
X  struct {
X    unsigned int start,
X		 stop;
X  } batch;			/* When to batch requests */
X  struct {
X    char prog [MAX_LINE],	/* Faxing program */
X	 fax_no [MAX_LINE];	/* Fax number */
X  } fax;
X  char serverd_cmdoptions [MAX_LINE],/* Command line options */
X       manager [MAX_LINE],	/* Manager of the system */
X       arg [MAX_LINE],		/* Temporary storage */
X       organization [MAX_LINE]; /* Organization's name */
X       int users,		/* Used by listserv when -r is given */
X       frequency;		/* How often to read mail */
X  long int options;		/* Various flags: BSD_PS, USE_TELNET, etc. */
X} SYS;
X
Xtypedef void (*FUNC)();
X
Xtypedef struct {
X  char *name;
X  long int mask;
X  FUNC func;
X} COMMANDS;			/* Structure for requests */
X
Xtypedef struct remote {
X  char alias [MAX_LINE],
X       address [MAX_LINE],
X       comment [MAX_LINE],
X       listserv [MAX_LINE],
X       inet_addr [MAX_LINE];
X  int  port;
X  struct remote *next;
X} REMOTE;			/* Structure for remote lists */
*-*-END-of-src/struct.h-*-*
echo x - src/sysmail.h
sed 's/^X//' >src/sysmail.h <<'*-*-END-of-src/sysmail.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991/92, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#ifdef __STDC__
X# include "ansi/sysmail.h"
X#else
X# include "nonansi/sysmail.h"
X#endif
X
X#include <errno.h>
X
X#define PORT		  25	/* sendmail port */
X#define TIMEOUT	 	  600	/* 10 mins timeout connecting to sendmail */
X#define CONN_TIMEOUT	  -1
X#define MAX_CALLS	  1	/* if > 1, retry delivery that many times */
X#ifndef SENDMAIL_HOST
X# define SENDMAIL_HOST	  "localhost"
X#endif
X
X/* List of SMTP commands recognized */
X
X#define ACKNOWLEDGE	  220
X#define CLOSE_CONNECTION  221
X#define OK                250
X#define WILL_FORWARD      251
X#define DATA		  354
X#define SERVICE_UNAVAIL	  421
X#define PERMISSION_DENIED 450
X#define HOST_ABORTED	  451
X#define DISK_FULL	  452
X#define NOT_RECOGNIZED    500
X#define SYNTAX_ERROR      501
X#define CMD_NOT_IMPL	  502
X#define BAD_SEQUENCE	  503
X#define PARAM_NOT_IMPL    504
X#define USER_UNKNOWN      550
X#define USER_NOT_LOCAL    551
X#define TOO_MUCH_DATA	  552
X#define USER_AMBIGUOUS    553
X#define TRANS_FAILED  	  554
X#define END_OF_TEXT	  ".\n"
X
X#define CLOSEF \
X  fclose (msg),\
X  close (sock_fd)
X
X#define NOTIFY_MANAGER(error) \
X  if (sys.options & BSD_MAIL)\
X    syscom ("echo '%s' | %s -s 'Error during message delivery: ' %s &",\
X	    error, UCB_MAIL, sys.manager)
X
X#define WARN_MANAGER_AND_OWNER(warning) \
X  if (sys.options & BSD_MAIL) {\
X    syscom ("echo '%s' | %s -s '%s: Warning during message delivery: ' %s &",\
X	    warning, UCB_MAIL,\
X(listid < 0 ? "LISTSERV" : sys.lists[listid].alias), sys.manager);\
X    if (listid >= 0 && strcmp (sys.manager, sys.lists[listid].owner))\
X      syscom ("echo '%s' | %s -s '%s: Warning during message delivery: ' %s &",\
X  	      warning, UCB_MAIL,\
X(listid < 0 ? "LISTSERV" : sys.lists[listid].alias), sys.lists[listid].owner);\
X  }
X
X#define ABORT_CONNECTION \
X  if ((int) write (sock_fd, "QUIT\n", 5) < 5)\
X    report_progress (report, "_sysmail(): WARNING: Error writing to \
Xsocket, while closing connection", TRUE);\
X  if (debug)\
X    fprintf (sent, "QUIT\n[ transaction aborted ]\n"),\
X    fflush (sent);\
X  cmd = server_response (sock_fd);
X
X#define CHECK_SERVER_RESPONSE(cmd, arg, cont)\
X  if (cmd != OK && cmd != arg) {\
X    char error [1024];\
X    if (cmd == SYNTAX_ERROR || cmd == TRANS_FAILED ||\
X        cmd == NOT_RECOGNIZED) {\
X      sprintf (error, "_sysmail(): Command: %snot recognized by sendmail.\
X\n%s\nCould not deliver mail", buf, message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == CMD_NOT_IMPL || cmd == PARAM_NOT_IMPL) {\
X      sprintf (error, "_sysmail(): Command: %snot implemeted.\n%s\n\
XCould not deliver mail", buf, message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == BAD_SEQUENCE) {\
X      sprintf (error, "_sysmail(): Command: %sissued too soon.\
X\n%s\nCould not deliver mail", buf, message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == SERVICE_UNAVAIL || cmd == HOST_ABORTED) {\
X      sprintf (error, "_sysmail(): Service unavailable.\
X\n%s\nCould not deliver mail", message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == DISK_FULL) {\
X      sprintf (error, "_sysmail(): File system full.\n%s\nCould not \
Xdeliver mail", message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == TOO_MUCH_DATA) {\
X      sprintf (error, "_sysmail(): Too many recipients or message \
Xtoo long.\n%s\nCould not deliver mail", message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      ABORT_CONNECTION;\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == CONN_TIMEOUT) {\
X      sprintf (error, "_sysmail(): Connection timed out with %s. \
XCould not deliver mail", SENDMAIL_HOST);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    else if (cmd == USER_UNKNOWN) {\
X      sprintf (error, "_sysmail(): Address: %snot recognized.\n%s\nMessage \
Xnot delivered to this recipient", buf, message);\
X      report_progress (report, error, TRUE);\
X      WARN_MANAGER_AND_OWNER (error);\
X      cont;\
X    }\
X    else if (cmd == USER_NOT_LOCAL) {\
X      sprintf (error, "_sysmail(): Address: %snot local; %s", buf, message);\
X      report_progress (report, error, TRUE);\
X      WARN_MANAGER_AND_OWNER (error);\
X      cont;\
X    }\
X    else if (cmd == USER_AMBIGUOUS) {\
X      sprintf (error, "_sysmail(): Address ambiguous: %s%s\nMessage not \
Xdelivered to this address", buf, message);\
X      report_progress (report, error, TRUE);\
X      WARN_MANAGER_AND_OWNER (error);\
X      cont;\
X    }\
X    else if (cmd == PERMISSION_DENIED) {\
X      sprintf (error, "_sysmail(): Permission denied for address %s%s\n\
XMessage not delivered to this recipient", buf, message);\
X      report_progress (report, error, TRUE);\
X      WARN_MANAGER_AND_OWNER (error);\
X      cont;\
X    }\
X    else if (cmd == WILL_FORWARD) {\
X      sprintf (error, "_sysmail(): Address: %snot local; %s", buf, message);\
X      report_progress (report, error, TRUE);\
X      WARN_MANAGER_AND_OWNER (error);\
X      cont;\
X    }\
X    else {\
X      sprintf (error, "_sysmail(): ERROR: Return value %d for command: %s%s\n\
XPlease notify tasos@cs.bu.edu", cmd, buf, message);\
X      report_progress (report, error, TRUE);\
X      NOTIFY_MANAGER (error);\
X      if (call < MAX_CALLS)\
X        CLOSEF,\
X        sleep (60),\
X        _sysmail (file, call + 1);\
X      report_progress (report, "_sysmail(): ERROR: Could not deliver mail", \
XTRUE);\
X      CLOSEF,\
X      gexit (10);\
X    }\
X  }
X
X#define WRITE_TO_SOCKET(sock_fd, buf)\
X{ char bufcopy [1024];\
X  strcpy (bufcopy, buf);\
X  bytes_to_write = strlen (bufcopy);\
X  errno = 0;\
X  while ((bytes_written = write (sock_fd, bufcopy, strlen (bufcopy))) <\
X         bytes_to_write) {\
X    if (errno && errno != EWOULDBLOCK && errno != EAGAIN) {\
X      char error [MAX_LINE];\
X      switch (errno) {\
X	case EBADF: sprintf (error, "_sysmail(): Bad file number"); break;\
X	case EFAULT: sprintf (error, "_sysmail(): Bad address"); break;\
X	case EFBIG: sprintf (error, "_sysmail(): File limit reached"); break;\
X	case EINTR: sprintf (error, "_sysmail(): Received interrupt signal");\
X		    break;\
X	case EINVAL: sprintf (error, "_sysmail(): Negative seek pointer");\
X		     break;\
X	case EIO: sprintf (error, "_sysmail(): I/O error"); break;\
X 	case ENOSPC: sprintf (error, "_sysmail(): No space left on device");\
X		     break;\
X	case ENXIO: sprintf (error, "_sysmail(): No such device or address");\
X		    break;\
X	case ERANGE: sprintf (error, "_sysmail(): Bytes to write (%d) out of \
Xrange", bytes_to_write);\
X		     break;\
X	default: sprintf (error, "_sysmail(): Error number %d", errno);\
X      }\
X      report_progress (report, error, TRUE);\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    if (debug) {\
X      for (i = 0; i < bytes_written; fprintf (sent, "%c", bufcopy [i++]));\
X      fflush (sent);\
X    }\
X    if (bytes_written > 0)\
X      bytes_to_write -= bytes_written;\
X    if (bytes_written > 0)\
X      sprintf (bufcopy, "%s", bufcopy + bytes_written);\
X    errno = 0;\
X  }\
X  if (debug)\
X    fprintf (sent, "%s", bufcopy),\
X    fflush (sent);\
X}
X
X#define PROTOCOL(arg) \
X  while (!feof (msg) && cmd != arg) {\
X    RESET (buf);\
X    fgets (buf, MAX_LINE - 2, msg);\
X    WRITE_TO_SOCKET (sock_fd, buf);\
X    if ((cmd = server_response (sock_fd)) == CONN_TIMEOUT) {\
X      queue = TRUE;\
X      goto abort;\
X    }\
X    CHECK_SERVER_RESPONSE (cmd, arg, continue);\
X  }
X
Xstatic char message [1024];
Xstatic FILE *sent, *received;
Xstatic BOOLEAN queue;
*-*-END-of-src/sysmail.h-*-*
echo x - src/ansi/catmail.h
sed 's/^X//' >src/ansi/catmail.h <<'*-*-END-of-src/ansi/catmail.h-*-*'
X#define LOST_REQUESTS		PATH "/lost+found"
X#define TAG_IDF			PATH "/.tag.id"
X#define	CATMAIL_TMP		PATH "/.catmail.tmp"
*-*-END-of-src/ansi/catmail.h-*-*
echo x - src/ansi/defs.h
sed 's/^X//' >src/ansi/defs.h <<'*-*-END-of-src/ansi/defs.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define PATH              "/usr/server"
X#define WARNING           PATH "/.warning"
X#define REPORT_SERVER     PATH "/.report.server"
X#define REPORT_SERVERD    PATH "/.report.daemon"
X#define REPORT_PQUEUE     PATH "/.report.pqueue"
X#define REPORT_CATMAIL	  PATH "/.report.catmai"
X#define CONFIG            PATH "/config"
X#define LIST              PATH "/list -1 -L "
X#define SERVER            PATH "/listserv -1 "
X#define SERVER_MAIL_FILE  PATH "/requests"
X#define LIVE_REQUESTS_F	  PATH "/requests.live"
X#define BATCH_FILE	  PATH "/batch"
X#define SERVERD           PATH "/serverd"
X#define PQUEUE            PATH "/pqueue"
X#define START             PATH "/start"
X#define OWNERSF           PATH "/owners"
X#define ALIASESF	  PATH "/.aliases"
X#define SERVERD_LOCK_FILE PATH "/.lock.serverd"
X#define LIST_LOCK_FILE    PATH "/.lock.list"
X#define SERVER_LOCK_FILE  PATH "/.lock.server"
X#define PQUEUE_LOCK_FILE  PATH "/.lock.pqueue"
X#define PID_PQUEUE        PATH "/.pid.pqueue"
X#define PID_SERVERD       PATH "/.pid.daemon"
X#define PID_SERVER        PATH "/.pid.server"
X#define PID_LIST          PATH "/.pid.list"
X#define PID_QUEUED	  PATH "/.pid.queued"
X#define MAILFORWARD       PATH "/.mailforward"
X#define ULISTSERVER_REPLY PATH "/.reply.listser"
X#define ARCHIVE_DIR       PATH "/archives"
X#define OLD_SUBSCRIBERS   PATH "/.oldsubscriber"
X#define OLD_ALIASES	  PATH "/.oldaliases"
X#define NEW_SUBSCRIBERS   PATH "/.newsubscriber"
X#define NEW_ALIASES	  PATH "/.newaliases"
*-*-END-of-src/ansi/defs.h-*-*
echo x - src/ansi/listserv.h
sed 's/^X//' >src/ansi/listserv.h <<'*-*-END-of-src/ansi/listserv.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define MAIL_COPY         PATH "/.messages"
X#define MSG_NO            PATH "/.msgno"
X#define AWK_PROG          PATH "/.awk"
X#define STATS_PROG        PATH "/.stats"
X#define SERVER_MBOX       PATH "/mbox"    /* place to save server's messages */
X#define HELP_TOPICS	  PATH "/help/TOPICS" /* Index of topics and files */
X#define STDOUT		  PATH "/stdout"
X#define STDERR		  PATH "/stderr"
X#define SETUP_MESSAGE_IDSF sprintf (message_idsf, "%s/%s", PATH, MESSAGE_IDS_F)
X#define EXEC_STATS \
X  syscom ("%s %s %s %s %s '%s'", STATS_PROG, PATH, subscribersf,\
X          headersf, MAILFORWARD, params)
X
X#define REARRANGE_SUBSCRIBERS \
X  syscom ("cat %s | %s/rev | awk '{print $NF \"\\t\" $0}' | sort | \
Xsed 's/^.*\t//' | %s/rev > %s/sub.%s", subscribersf,\
X	  PATH, PATH, PATH, sys.lists[listid].alias);\
X  mv (tsprintf ("%s/sub.%s", PATH, sys.lists[listid].alias), subscribersf)
X
X#define CAT_FILE \
X  syscom ("%s %s | %s/fwin -n -o %ld -b %ld | sed -e 's/^From />From /' | \
Xsed -e 's/^\\.$/\\\\./' >> %s", \
X	  (compressed_source ? "zcat" : "cat"), fullname, PATH, offset,\
X	  nbytes, MAILFORWARD)
*-*-END-of-src/ansi/listserv.h-*-*
echo x - src/ansi/misc.h
sed 's/^X//' >src/ansi/misc.h <<'*-*-END-of-src/ansi/misc.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define SETUP_STRING sprintf (s, "%s/lists/%s/%s", PATH, alias, filename)
X#define SETUP_IGNOREDF sprintf (server_ignoredf, "%s/%s", PATH, IGNORED)
*-*-END-of-src/ansi/misc.h-*-*
echo x - src/ansi/serverd.h
sed 's/^X//' >src/ansi/serverd.h <<'*-*-END-of-src/ansi/serverd.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define LOAD_FILE         PATH "/.load"
X#define UNWANTED_HOSTSF	  PATH "/unwanted.hosts"
X#define PRIVILEGED_HOSTSF PATH "/priv.hosts"
X#define WELCOMEF	  PATH "/welcome.live"
*-*-END-of-src/ansi/serverd.h-*-*
echo x - src/ansi/start.h
sed 's/^X//' >src/ansi/start.h <<'*-*-END-of-src/ansi/start.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define REPORT_SERVER_ACC   PATH "/.rep.server.ac"
X#define REPORT_SERVERD_ACC  PATH "/.rep.serverd.a"
X#define REPORT_START_ACC    PATH "/.rep.start.acc"
X#define REPORT_PQUEUE_ACC   PATH "/.rep.pqueue.ac"
X#define REPORT_START        PATH "/.report.start"
X#define CATMAIL		    PATH "/catmail"
X#define SETUP_DIR sprintf (dir, "%s/lists/%s", PATH, sys.lists[i].alias)
*-*-END-of-src/ansi/start.h-*-*
echo x - src/ansi/sysmail.h
sed 's/^X//' >src/ansi/sysmail.h <<'*-*-END-of-src/ansi/sysmail.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define SENT              PATH "/sent"
X#define RECEIVED_         PATH "/received"
X#define IDF               PATH "/.queue.id"
X#define QUEUE_DIR         PATH "/mqueue"
*-*-END-of-src/ansi/sysmail.h-*-*
echo x - src/nonansi/catmail.h
sed 's/^X//' >src/nonansi/catmail.h <<'*-*-END-of-src/nonansi/catmail.h-*-*'
X#define LOST_REQUESTS		PATH/lost+found"
X#define TAG_IDF			PATH/.tag.id"
X#define CATMAIL_TMP             PATH/.catmail.tmp"
*-*-END-of-src/nonansi/catmail.h-*-*
echo x - src/nonansi/defs.h
sed 's/^X//' >src/nonansi/defs.h <<'*-*-END-of-src/nonansi/defs.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define PATH              "/usr/server
X#define WARNING           PATH/.warning"
X#define REPORT_SERVER     PATH/.report.server"
X#define REPORT_SERVERD    PATH/.report.daemon"
X#define REPORT_PQUEUE     PATH/.report.pqueue"
X#define REPORT_CATMAIL	  PATH/.report.catmai"
X#define CONFIG            PATH/config"
X#define LIST              PATH/list -1 -L "
X#define SERVER            PATH/listserv -1 "
X#define SERVER_MAIL_FILE  PATH/requests"
X#define LIVE_REQUESTS_F   PATH/requests.live"
X#define BATCH_FILE	  PATH/batch"
X#define SERVERD           PATH/serverd"
X#define PQUEUE            PATH/pqueue"
X#define START             PATH/start"/* Do not give ANY command line options */
X#define OWNERSF           PATH/owners"
X#define ALIASESF	  PATH/.aliases"
X#define SERVERD_LOCK_FILE PATH/.lock.serverd"
X#define LIST_LOCK_FILE    PATH/.lock.list"
X#define SERVER_LOCK_FILE  PATH/.lock.server"
X#define PQUEUE_LOCK_FILE  PATH/.lock.pqueue"
X#define PID_PQUEUE        PATH/.pid.pqueue"
X#define PID_SERVERD       PATH/.pid.daemon"
X#define PID_SERVER        PATH/.pid.server"
X#define PID_LIST          PATH/.pid.list"
X#define PID_QUEUED	  PATH/.pid.queued"
X#define MAILFORWARD       PATH/.mailforward"
X#define ULISTSERVER_REPLY PATH/.reply.listser"
X#define ARCHIVE_DIR	  PATH/archives"
X#define OLD_SUBSCRIBERS   PATH/.oldsubscriber"
X#define OLD_ALIASES	  PATH/.oldaliases"
X#define NEW_SUBSCRIBERS   PATH/.newsubscriber"
X#define NEW_ALIASES	  PATH/.newaliases"
*-*-END-of-src/nonansi/defs.h-*-*
echo x - src/nonansi/listserv.h
sed 's/^X//' >src/nonansi/listserv.h <<'*-*-END-of-src/nonansi/listserv.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for:w profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define MAIL_COPY         PATH/.messages"
X#define MSG_NO            PATH/.msgno"
X#define AWK_PROG          PATH/.awk"
X#define STATS_PROG        PATH/.stats"
X#define SERVER_MBOX       PATH/mbox"    /* place to save server's messages */
X#define HELP_TOPICS       PATH/help/TOPICS" /* Index of topics and files */
X#define STDOUT		  PATH/stdout"
X#define STDERR		  PATH/stderr"
X#define SETUP_MESSAGE_IDSF sprintf (message_idsf, "%s/%s", PATH", MESSAGE_IDS_F)
X#define EXEC_STATS \
X  syscom ("%s %s %s %s %s '%s'", STATS_PROG, PATH", subscribersf,\
X          headersf, MAILFORWARD, params)
X
X#define REARRANGE_SUBSCRIBERS \
X  syscom ("cat %s | %s/rev | awk '{print $NF \"\\t\" $0}' | sort | \
Xsed 's/^.*\t//' | %s/rev > %s/sub.%s", subscribersf,\
X	  PATH", PATH", PATH", sys.lists[listid].alias);\
X  mv (tsprintf ("%s/sub.%s", PATH", sys.lists[listid].alias), subscribersf)
X
X#define CAT_FILE \
X  syscom ("%s %s | %s/fwin -n -o %ld -b %ld | sed -e 's/^From />From /' | \
Xsed -e 's/^\\.$/\\\\./' >> %s", \
X	  (compressed_source ? "zcat" : "cat"), fullname, PATH", offset,\
X          nbytes, MAILFORWARD)
*-*-END-of-src/nonansi/listserv.h-*-*
echo x - src/nonansi/misc.h
sed 's/^X//' >src/nonansi/misc.h <<'*-*-END-of-src/nonansi/misc.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define SETUP_STRING sprintf (s, "%s/lists/%s/%s", PATH", alias, filename)
X#define SETUP_IGNOREDF sprintf (server_ignoredf, "%s/%s", PATH", IGNORED)
*-*-END-of-src/nonansi/misc.h-*-*
echo x - src/nonansi/serverd.h
sed 's/^X//' >src/nonansi/serverd.h <<'*-*-END-of-src/nonansi/serverd.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define LOAD_FILE         PATH/.load"
X#define UNWANTED_HOSTSF	  PATH/unwanted.hosts"
X#define PRIVILEGED_HOSTSF PATH/priv.hosts"
X#define WELCOMEF	  PATH/welcome.live"
*-*-END-of-src/nonansi/serverd.h-*-*
echo x - src/nonansi/start.h
sed 's/^X//' >src/nonansi/start.h <<'*-*-END-of-src/nonansi/start.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define REPORT_SERVER_ACC   PATH/.rep.server.ac"
X#define REPORT_SERVERD_ACC  PATH/.rep.serverd.a"
X#define REPORT_START_ACC    PATH/.rep.start.acc"
X#define REPORT_PQUEUE_ACC   PATH/.rep.pqueue.ac"
X#define REPORT_START        PATH/.report.start"
X#define CATMAIL		    PATH/catmail"
X#define SETUP_DIR sprintf (dir, "%s/lists/%s", PATH", sys.lists[i].alias)
*-*-END-of-src/nonansi/start.h-*-*
echo x - src/nonansi/sysmail.h
sed 's/^X//' >src/nonansi/sysmail.h <<'*-*-END-of-src/nonansi/sysmail.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely only as a
X  whole and not in parts, as long as you do not remove or alter the author
X  and copyright notices in the file defs.h; this notices are #define'd in
X  the symbols VERSION and COPYRIGHT. Although you may alter the code
X  provided for your personal use, you you may not alter the functions
X  create_header(), create_multi_recipient_header() and main() in list.c,
X  listserv.c and serverd.c (where applicable), and you may not redistribute
X  any changes you may have made. No part of the source code bearing a
X  copyright notice can be included in commercial software systems without
X  written permission by the author.
X  By using this software you are bound by this agreement.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to the
X  restrictions set forth in FAR 52.227-19(c), Commercial Computer Software or,
X  for Department of Defense Users, by DFAR 252.227-7013(c)(1)(ii).
X*/
X
X#define SENT              PATH/sent"
X#define RECEIVED_         PATH/received"
X#define IDF               PATH/.queue.id"
X#define QUEUE_DIR         PATH/mqueue"
*-*-END-of-src/nonansi/sysmail.h-*-*
echo x - src/catmail.c
sed 's/^X//' >src/catmail.c <<'*-*-END-of-src/catmail.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |				CATMAIL UTILITY				     |
X  |									     |
X  |				  version 2.5				     |
X  |									     |
X  | User contributed application.					     |
X  | Original author (1.0): Aad Nienhuis <Aad_Nienhuis@sconar.sco.uva.nl>     |
X  | Modified by (1.1): Tasos Kotsikonas					     |
X  | Enhanced by (2.0): Tasos Kotsikonas, Warren Burstein		     |
X  | Enhanced by (2.1): Tasos Kotsikonas
X  ----------------------------------------------------------------------------
X
X  This is a utility that appends a mail message supplied by sendmail in 
X  stdin to the appropriate file, depending on the command line options
X  supplied. The application has to be setuid. If the incoming message
X  is for a moderated list and not from the moderator, append a NOTIFY request
X  to the requests file so that listserv will later notify the list owner,
X  soliciting his approval for posting the message.
X
X  The utility is to be used primarily in the host's aliases file when 
X  setting up aliases for various lists. To append to the system's requests
X  file it may be used as follows:
X
X    listserver: "|HOMEDIR/catmail -r -f"
X
X  To append to a list's mail file:
X
X    list_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f"
X
X  To append to a list's moderated file:
X
X    list_alias: "|HOMEDIR/catmail -L LIST_ALIAS -f -m"
X
X  COMMAND LINE OPTIONS:
X    -L: The input is appened to the specified list's "mail" file.
X    -l: Same as -L.
X    -m: The input is appened to the specified list's "moderated" file.
X	and a copy is appended to the requests file to be forwarded
X	by listserv to the owner for approval.
X    -r: The input is appened to HOMEDIR/requests.
X    -f: The input is reformatted while appended.
X*/
X
X#include <stdio.h>
X#include <stdio.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <time.h>
X#include "defs.h"
X#include "catmail.h"
X#include "struct.h"
X#include "global.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xextern char *tsprintf ();
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern void setup_string (char *, char *, char *);
Xextern int  _getopt (int, char **, char *);
Xextern char *upcase (char *);
Xextern int  get_list_id (char *, SYS *, int);
Xextern void report_progress (FILE *, char *, int);
Xextern int  lock_file (char *, int, int, BOOLEAN);
Xextern void unlock_file (int);
Xextern BOOLEAN extract_sender (char *);
Xextern BOOLEAN owner_listed (char *, char *, char *, FILE *);
Xextern int  otoi (char *);
Xextern int cat_append (char *, char *);
Xextern int echo (char *, char *);
Xextern BOOLEAN strinstr (char *, char *);
X
Xvoid   main (int, char **, char **);
Xvoid   catmail (FILE *, FILE *, FILE *, FILE *, int);
Xint    get_tag_id (void);
Xvoid   usage (void);
Xint    gexit (int);
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  char *options = "rfml:L:", *getenv();
X  extern char *optarg;
X  extern int optopt;
X  long int t;
X  int c, nlists, tag = 0;
X  char *list_alias = NULL, *mask;
X  char file [MAX_LINE];
X  char header [MAX_LINE];
X  char msg [MAX_LINE];
X  FILE *mail, *mailf = NULL, *pipe = NULL;
X  struct passwd *pwentry;
X
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case 'm': moderated = TRUE; break;
X      case 'f': reformat = TRUE; break;
X      case 'r': requests = TRUE; moderated = FALSE; break;
X      case 'l':
X      case 'L': list_alias = upcase (optarg); break;
X      case ':':
X	fprintf (stderr, "catmail: Option '%c' requires an argument.\n",
X		 optopt);
X	exit (3);
X      case '?':
X      default:
X        usage();
X    }
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  if ((report = fopen (REPORT_CATMAIL, "a+")) == NULL)
X    fprintf (stderr, "catmail: Could not open %s\n", REPORT_CATMAIL),
X    exit (1);
X  chmod (REPORT_CATMAIL, 384);
X#endif
X
X  if ((pwentry = getpwuid (getuid ())) == NULL)
X    report_progress (report, "main(): Invalid user account", TRUE),
X    exit (3);
X  if (requests)		/* Select file to append to */
X    tty_echo = FALSE,
X    report_progress (report, "\n--- NEW MAIL FOR LISTSERV ---\n", FALSE),
X    report_progress (report, tsprintf ("Access granted to user id %d (%s)",
X				       getuid(), pwentry->pw_name), TRUE),
X    strcpy (file, SERVER_MAIL_FILE);
X  else {
X    if (list_alias == NULL)
X      report_progress (report, "\ncatmail: No list to process", TRUE),
X      exit (3);
X    nlists = sys_config (report, &sys);
X    if ((listid = get_list_id (list_alias, &sys, nlists)) < 0)
X      report_progress (report, tsprintf ("\ncatmail: Unknown list %s",
X					 list_alias), TRUE),
X      exit (3);
X    setup_string (file, list_alias,
X     (moderated ? LIST_MODERATED_F : LIST_MAIL_FILE));
X    tty_echo = FALSE;
X    report_progress (report, tsprintf ("\n--- NEW MAIL FOR %s ---\n",
X				       list_alias), FALSE);
X    report_progress (report, tsprintf ("Access granted to user id %d (%s)",
X				       getuid(), pwentry->pw_name), TRUE);
X    setup_string (list_mail_f, list_alias, LIST_MAIL_FILE);
X  }
X#ifndef NO_LOCKS
X  signal (SIGINT, (void (*)()) gexit);
X  if ((lfd = lock_file (file, O_RDWR | O_CREAT, 0640, TRUE)) < 0)
X    switch (lfd) {
X    case CANT_OPEN:
X      report_progress (report, tsprintf ("\nCould not stat file %s", file),
X		       TRUE);
X      exit (1);
X    case CANT_LOCK:
X      if (requests)
X	strcpy (file, LOST_REQUESTS);
X      else
X        setup_string (file, list_alias, LOST_MAIL);
X      report_progress (report, tsprintf ("\nCANNOT LOCK FILE: SAVING UNDER %s",
X					 file), TRUE);
X    }
X  if (moderated) { /* Also lock SERVER_MAIL_FILE and LIST_MAIL_FILE */
X    if ((lfd2 = lock_file (SERVER_MAIL_FILE, O_RDWR | O_CREAT, 0640, TRUE)) < 0)
X      switch (lfd2) {
X      case CANT_OPEN:
X        report_progress (report, tsprintf ("\nCould not stat file %s",
X					   SERVER_MAIL_FILE), TRUE);
X        gexit (1);
X      case CANT_LOCK:
X        report_progress (report,
X			 tsprintf ("\nCANNOT LOCK FILE %s: TAKING MY CHANCES",
X				   SERVER_MAIL_FILE), TRUE);
X      }
X    if ((lfd3 = lock_file (list_mail_f, O_RDWR | O_CREAT, 0640, TRUE)) < 0)
X      switch (lfd3) {
X      case CANT_OPEN:
X        report_progress (report, tsprintf ("\nCould not stat file %s",
X					   list_mail_f), TRUE);
X        gexit (1);
X      case CANT_LOCK:
X        report_progress (report,
X			 tsprintf ("\nCANNOT LOCK FILE %s: TAKING MY CHANCES",
X				   list_mail_f), TRUE);
X      }
X  }
X#endif
X  OPEN_FILE (mail, file, "a+", "main");
X  sprintf (header, "/tmp/.%d.1.catmail", getpid());
X  sprintf (msg, "/tmp/.%d.2.catmail", getpid());
X  if (moderated) {
X    tag = get_tag_id();
X    OPEN_FILE (pipe, header, "w", "main");
X    t = time (0);
X    fprintf (pipe, "From %s %s\nNOTIFY\n\
XApproval request from %s for posting \
Xthe following\nmessage to moderated list %s. If approved, send the following \
Xrequest to\n%s:\n\nAPPROVE %s <password> %d\n\n\
XIf the message is to be discarded, reply with the following request:\n\n\
XDISCARD %s <password> %d\n\n\
X----------------------------- message follows ------------------------------\n",
X	     sys.lists[listid].owner, ctime (&t), sys.server.address,
X	     list_alias, sys.server.address, list_alias, tag, list_alias, tag);
X    fclose (pipe);
X    OPEN_FILE (pipe, msg, "w", "main");
X    OPEN_FILE (mailf, list_mail_f, "a+", "main");
X  }
X  catmail (stdin, mail, mailf, pipe, tag);
X  fclose (mail);
X  if (pipe)
X    fclose (pipe);
X  if (mailf)
X    fclose (mailf);
X#ifdef SYSLOG
X  closelog ();
X#else
X  fclose (report);
X#endif
X  if (moderated) /* Message was not from moderator */
X    cat_append (header, SERVER_MAIL_FILE),
X    cat_append (msg, SERVER_MAIL_FILE);
X  unlink (header);
X  unlink (msg);
X  gexit (0);
X}
X
X/*
X  Append the input file to the output file, possibly pipe it also to another
X  output file, and possibly reformat it.
X  Turn moderation flag off if the message is from one of the list owners.
X*/
X
Xvoid catmail (FILE *in, FILE *out, FILE *mailf, FILE *copy, int tag)
X{
X  char buf [MAX_LINE];
X  char sender [MAX_LINE];
X
X  sender[0] = RESET (buf);
X  fgets (buf, MAX_LINE - 2, in);  /* Do not format very first "From " line */
X  if (feof (in)) {
X    moderated = FALSE;
X    return;
X  }
X  strcpy (sender, buf);
X
X  extract_sender (sender); /* Get sender; ignore address syntax correctness */
X  upcase (sender);
X  if (moderated &&
X      (owner_listed (OWNERSF, sender, sys.lists[listid].alias, report) ||
X       strinstr (MAILER_DAEMON, sender)))
X    out = mailf, /* Output file is now 'mail' instead of 'moderated' */
X    moderated = FALSE; /* Message from moderator */
X  fputs (buf, out);
X  if (moderated && !strncmp (buf, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))
X    fprintf (out, "Message-Tag: %d\n", tag),
X    fprintf (copy, ">%sMessage-Tag: %d\n", buf, tag);
X  RESET (buf);
X  while (!feof (in)) {
X    fgets (buf, MAX_LINE - 2, in);
X    if (buf[0] != EOS) {
X      if (reformat &&
X	  !strncmp (buf, START_OF_MESSAGE, strlen (START_OF_MESSAGE))) {
X        putc ('>', out);
X	if (copy)
X	  putc ('>', copy);
X      }
X      fputs (buf, out);
X      if (copy)
X	fputs (buf, copy);
X    }
X    RESET (buf);
X  }
X}
X
X/*
X  Get unique tag id for moderated message.
X*/
X
Xint get_tag_id ()
X{
X  FILE *id;
X  int id_no = 1;
X
X  if ((id = fopen (TAG_IDF, "r")) != NULL)
X    fscanf (id, "%d\n", &id_no),
X    fclose (id);
X  echo (tsprintf ("%d", id_no + 1), TAG_IDF);
X  return id_no;
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: catmail {<<-L | -l> LIST_ALIAS [-m]> | <-r>} [-f]]\n\
X-L: Append to HOMEDIR/lists/LIST_ALIAS/mail.\n\
X-l: Same -L.\n\
X-m: Append to HOMEDIR/lists/LIST_ALIAS/moderated instead.\n\
X-r: Append to HOMEDIR/requests instead.\n\
X-f: Reformat messages before appending.\n\
X");
X  exit (3);
X}
X
X/*
X  Graceful exit.
X*/
X
Xint gexit (int exitcode)
X{
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X  unlock_file (lfd2);
X  unlock_file (lfd3);
X#endif
X  exit (exitcode);
X}
*-*-END-of-src/catmail.c-*-*
echo x - src/farch.c
sed 's/^X//' >src/farch.c <<'*-*-END-of-src/farch.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                	    FILE ARCHIVING UTILITY			     |
X  |                                                                          |
X  |                               Version 3.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer	     |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).								     |
X  ----------------------------------------------------------------------------
X
X  farch archives files into the specified directories, possibly splitting
X  them into smaller parts. It then updates the DIR file of the appropriate
X  archive. Split files have numbers appended to their original names;
X  non-split files have their names intact. Binary files are uuencoded before
X  processed, and all output files are compressed.
X
X  COMMAND LINE OPTIONS:
X    -n: Do not split input files.
X    -t: Tar all input files.
X    -b: Input files are binary; uuencode.
X    -B: Input files are binary; do not uuencode, do not split.
X    -s: Set the maximum size of each of the split subparts in kilobytes.
X    -d: Specify the actual directory that the archived files will reside.
X    -p: Specify the password for the private archive.
X    -D: Specify a descriptive string to be appended to each DIR file.
X    -r: Remove the specified files from the specified archive.
X    -a: Specify the archive where the input files will be archived.
X    -u: Print usage.
X
X  WARNING: The program can also archive directory files, if these can be
X  of any use.
X
X  Algorithm:
X  {
X    Process command line arguments; allocate buffer_size
X    Open master archive and all subarchives as necessary to get to the
X      specified (path-to-) archive; get path to specified archive
X    Verify existsence of target directory -- create it if necessary
X    Tar files, if so requested
X    for (each input file) {
X      open
X      get filesize
X      if (-r)
X        remove file
X      else {
X        if (-b)
X	  uuencode file
X        if (-n or filesize <= buffer_size) { -- Do not split --
X	  if (-b)
X	    copy uuencoded file into target directory
X	  if (file does not exist in target directory)
X	    copy it to target directory
X	    compress it
X	  update (archive's directory)
X        }
X        else split file {
X	  while (not eof) {
X	    get unique output filename
X	    fwrite (leftover bytes from previous chunk)
X	    fread (new chunk)
X	    if (! -b) {
X	      locate nearest newline from the end of the buffer
X	      copy those skipped bytes to leftover
X	    }
X	    fwrite (new modified chunk)
X	    compress it
X	  }
X	  fwrite (any leftovers)
X	  compress part
X	  update (archive's directory)
X        }
X      }
X    }
X  }
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/stat.h>
X#include <string.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <signal.h>
X#include "defs.h"
X#include "struct.h"
X#include "global.h"
X#include "listserv.h"
X
X#define DEFAULT_SIZE    64 * 1024
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
X#else
X# include <varargs.h>
Xextern int  syscom ();
X#endif
Xextern char *extract_filename (char *);
Xextern char *locase (char *);
X#ifndef __NeXT__
Xextern long int atol (char *);
X#else
Xextern long int atol (const char *);
X#endif
Xextern int  _getopt (int, char **, char *);
Xextern BOOLEAN make_indexes (char *, char *, char *, char *, char *);
Xextern BOOLEAN mkdir1 (char *, char *, char *);
Xextern char *mystrdup (char *);
X
Xvoid   main (int , char **);
Xvoid   uuencode (FILE **, char *, char *, struct stat *, BOOLEAN);
Xint    update (char *, char *, long int, long int *, char *, char *,
X	       struct stat, BOOLEAN);
Xvoid   remove_files (char *, char *, long int, struct stat);
Xvoid   usage (void);
Xvoid   do_archive (int, int, char **, char *, char *, char *, char *, char *,
X		   long int, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN);
Xint    get_archive (char **, char *, char *, BOOLEAN);
Xint    get_dir (char *, char **, char *);
Xint    tar_files (int, int, char **, char *);
Xvoid   rmfiles (int, int, char **, char *);
Xint    gexit (int);
X
Xchar *uue_file;
X
Xvoid main (int argc, char **argv)
X{
X  long int size = DEFAULT_SIZE;
X  char *buf, *arch = NULL, *newdir = NULL, *dir, fullpath[10240],
X       *tarf, *password = NULL, *options = "nbBs:a:d:t:p:D:ur", *text = "";
X  int c;
X  BOOLEAN bin = FALSE, tar = FALSE, split = TRUE, uue = FALSE,
X    remove_files = FALSE;
X  extern char *optarg;
X  extern int optopt, optind;
X
X  buf = (char *) malloc (size * sizeof (char));
X  dir = (char *) malloc (sizeof (char));
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X    case 'n': split = FALSE; break;
X    case 't': tar = TRUE; tarf = optarg;
X    case 'b': bin = TRUE; uue = TRUE; break;
X    case 'B': bin = TRUE; uue = FALSE; split = FALSE; break;
X    case 's': size = (long) atol (optarg) * 1024;
X      	      if (size <= 0)
X                fprintf (stderr, "How am I supposed to allocate %ld bytes?\n",
X                         size),
X                exit (3);
X              free ((char *) buf);
X              if ((buf = (char *) malloc (size * sizeof (char))) == NULL)
X                fprintf (stderr, "Size too big; not enough memory.\n"),
X                exit (11);
X              break;
X    case 'a': arch = locase (optarg); break;
X    case 'd': newdir = optarg;
X              if (*newdir != '/')
X                fprintf (stderr, "Must specify full path for the directory.\n"),
X                exit (3);
X              break;
X    case 'p': password = locase (optarg); break;
X    case 'D': text = optarg; break;
X    case 'r': remove_files = TRUE; break;
X    case 'u': usage ();
X    case ':': fprintf (stderr, "Option '%c' requires an argument.\n", optopt);
X              exit (3);
X    case '?':
X    default:
X      fprintf (stderr, "Unrecognized option '%c'.\n", optopt);
X      usage ();
X    }
X
X  if (optind == argc) {
X    fprintf (stderr, "farch: filename(s) missing.\n");
X    goto abort;
X  }
X  if (get_archive (&arch, fullpath, password, remove_files) &&
X      (remove_files || get_dir (newdir, &dir, fullpath))) {
X    if (remove_files)
X      rmfiles (optind, argc, argv, fullpath);
X    else {
X      if (tar)
X	if (tar_files (optind, argc, argv, tarf))
X	  argc = optind + 1;
X	else 
X	  goto abort;
X      do_archive (optind, argc, argv, buf, fullpath, arch, dir, text, size, bin,
X		  uue, split, tar);
X      if (tar)
X	unlink (tarf);
X    }
X  }
X  abort:
X  free ((char *) dir);
X  free ((char *) buf);
X}
X
X/*
X  uuencode a file.
X*/
X
Xvoid uuencode (FILE **fin, char *file, char *infile, struct stat *finbuf,
X	       BOOLEAN tar)
X{
X  fclose (*fin);
X  printf ("\t- uuencoding...\n");
X  syscom ("uuencode %s %s%s > %s", file, infile, (tar ? ".tar" : ""),
X	  (uue_file = mystrdup (tmpnam (NULL))));
X  *fin = fopen (uue_file, "r");
X  stat (uue_file, finbuf);
X}
X
X/*
X  tar all input files into 'tarf'.
X*/
X
Xint tar_files (int optind, int argc, char **argv, char *tarf)
X{
X  FILE *fin, *fout;
X  char files[10000];
X  int original = optind;
X
X  if ((fout = fopen (tarf, "w")) == NULL) {
X    fprintf (stderr, "Cannot write to tar file %s\n", tarf);
X    return 0;
X  }
X  fclose (fout);
X  unlink (tarf);
X  printf ("tarring...\n");
X  RESET (files);
X  while (optind < argc) {
X    if ((fin = fopen (argv[optind], "r")) == NULL) {
X      fprintf (stderr, "Unable to open %s for reading.\n", argv[optind]);
X      return 0;
X    }
X    fclose (fin);
X    sprintf (files + strlen (files), "%s ", argv[optind++]);
X  }
X  syscom ("tar -cf %s %s", tarf, files);
X  argv[original] = tarf;
X  return 1;
X}
X
X/*
X  Remove the file from the archive, if found.
X*/
X
Xvoid rmfiles (int optind, int argc, char **argv, char *path)
X{
X  char dirf[10240];
X  char file[10240];
X  char fullpath[10240];
X  char _fullpath[10240];
X  char fullname[10240];
X  char desc[10240];
X  char *s, *tmpdir;
X  BOOLEAN continued, found, match;
X  int cnt, _cnt, i;
X  long int count;
X  FILE *fin, *fout;
X  struct stat sbuf;
X
X  while (optind < argc) { /* Process each file */
X
X    s = argv[optind] + strlen (argv[optind]) - 1;
X    while (s != argv[optind] && *s == '/')  /* Remove trailing / */
X      *(s--) = EOS;
X
X    i = strlen (argv[optind]);
X    if (i > 2 && argv[optind][i - 2] == '.' && argv[optind][i - 1] == 'Z')
X      argv[optind][i - 2] = EOS;
X
X    printf ("%s: ", argv[optind]);
X    fflush (stdout);
X    RESET (dirf);
X    sprintf (dirf, "%s/%s", path, DIRF);
X    if (stat (dirf, &sbuf)) {
X      printf ("not removed\n");
X      fprintf (stderr, "Cannot access index %s for reading.\n", dirf);
X      exit (1);
X    }
X    signal (SIGINT, SIG_IGN);
X    syscom ("mv %s %s", dirf, (tmpdir = mystrdup (tmpnam (NULL))));
X    if ((fout = fopen (dirf, "w")) == NULL) {
X      fprintf (stderr, "Unable to open %s for writing.\nSEVERE ERROR: The \
Xarchive is now in an inconsistent state with no DIR file.\nIts DIR file can be \
Xfound in /tmp. Move %s to %s\n", dirf, tmpdir, dirf);
X      free ((char *) tmpdir);
X      exit (1);
X    }
X    if ((fin = fopen (tmpdir, "r")) == NULL) { 
X      fprintf (stderr, "Unable to open %s for reading. Cannot proceed.\n",
X	       tmpdir);
X      if (stat (dirf, &sbuf))
X	fputs ("SEVERE ERROR: The archive now has NO DIR file; DIR file \
Xlost!!!\n", stderr);
X      else
X	fputs ("Archive intact!\n", stderr);
X      free ((char *) tmpdir);
X      exit (1);
X    }
X    RESET (fullpath);
X    found = FALSE;
X    while (!feof (fin)) { /* Get location and file-count of file */
X      file[0] = RESET (_fullpath);
X      fscanf (fin, "%s %d ", file, &_cnt);
X      match = FALSE;
X      if (file[0] != EOS) {
X	locase (file);
X	if (!strcmp (argv[optind], file))
X	  match = found = TRUE;
X	if (!match)
X	  fprintf (fout, "%s %d ", file, _cnt);
X	for (i = 0; i < abs (_cnt); ++i) {
X	  fscanf (fin, "%ld ", &count);
X	  if (!match)
X	    fprintf (fout, "%ld ", count);
X	}
X	fscanf (fin, "%s", _fullpath);
X	if (!match)
X	  fprintf (fout, "%s", _fullpath);
X	else
X	  cnt = _cnt,
X	  strcpy (fullpath, _fullpath);
X	do { /* Get file description */
X	  RESET (desc);
X	  fgets (desc, MAX_LINE - 2, fin);
X	  if (desc [0] != EOS && desc[strlen (desc) - 1] == '\n')
X	    desc[strlen (desc) - 1] = EOS;
X	  continued = FALSE;
X	  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X	    desc[strlen (desc) - 1] = EOS,
X	    continued = TRUE;
X	  if (!match) {
X	    fprintf (fout, "%s", desc);
X	    if (continued)
X	      fprintf (fout, "\\");
X	    fputs ("\n", fout);
X	  }
X	} while (continued);
X      }
X    }
X    if (!found)
X      printf ("not found in %s", dirf);
X    else { /* Remove all parts */
X      printf ("removing parts: ");
X      fflush (stdout);
X      for (i = 1; i <= abs (cnt); i++) {
X	RESET (fullname);
X	if (abs (cnt) > 1)
X	  sprintf (fullname, "%s/%s%d", fullpath, argv[optind], i);
X	else
X	  sprintf (fullname, "%s/%s", fullpath, argv[optind]);
X	if (stat (fullname, &sbuf)) {
X	  strcat (fullname, ".Z"); /* Check for compressed file */
X	  if (stat (fullname, &sbuf))
X	    printf ("%d (not found) ", i),
X	    fflush (stdout);
X	  else {
X	    printf ("%d ", i);
X	    if (unlink (fullname))
X	      printf ("(errno %d) ", errno);
X	    fflush (stdout);
X	  }
X	}
X	else {
X	  printf ("%d ", i);
X	  if (unlink (fullname))
X	    printf ("(errno %d) ", errno);
X	  fflush (stdout);
X	}
X      }
X    }
X    fclose (fin);
X    fclose (fout);
X    signal (SIGINT, SIG_DFL);
X    ++optind;
X    puts ("");
X  }
X  unlink (tmpdir);
X  free ((char *) tmpdir);
X}
X
X/*
X  Check for duplicate file name and return if so. Otherwise, update the
X  archive's directory.
X*/
X
Xint update (char *path, char *filename, long int count, long int *filesizes,
X	    char *dir, char *text, struct stat finbuf, BOOLEAN pure_bin)
X{
X  char dirf[10240];
X  char file[10240];
X  char fullpath[10240];
X  char desc[10240];
X  char stripped_file[10240];
X  BOOLEAN continued, found = FALSE;
X  int cnt, i;
X  FILE *fout;
X
X  strcpy (stripped_file, filename);
X  i = strlen (stripped_file);
X  if (i > 2 && stripped_file [i - 2] == '.' && stripped_file [i - 1] == 'Z')
X    stripped_file [i - 2] = EOS;
X  RESET (dirf);
X  sprintf (dirf, "%s/%s", path, DIRF);
X  if ((fout = fopen (dirf, "r")) == NULL) {
X    fprintf (stderr, "Unable to open index %s for reading.\nFile %s \
Xnot archived. Remove output file(s) from %s\n", dirf, filename, dir);
X    exit (1);
X  }
X  while (!feof (fout)) { /* Get location and file-count of file */
X    file[0] = RESET (fullpath);
X    fscanf (fout, "%s %d %s", file, &cnt, fullpath);
X    do { /* Get file description */
X      RESET (desc);
X      fgets (desc, MAX_LINE - 2, fout);
X      if (desc [0] != EOS && desc[strlen (desc) - 1] == '\n')
X        desc[strlen (desc) - 1] = EOS;
X      continued = FALSE;
X      if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X        desc[strlen (desc) - 1] = EOS,
X        continued = TRUE;
X    } while (continued);
X    if (file[0] != EOS) {
X      locase (file);
X      if (!strcmp (stripped_file, file)) {
X        found = TRUE;
X        break;
X      }
X    }
X  }
X  fclose (fout);
X  if (found) {
X    fprintf (stderr, "\t- file %s already exists in %s\n\
X\t- file not archived\n",
X	     stripped_file, dirf);
X    remove_files (dir, filename, count, finbuf);
X    return 0;
X  }
X  signal (SIGINT, SIG_IGN);
X  if ((fout = fopen (dirf, "a")) == NULL) {
X    fprintf (stderr, "Unable to open index %s for update.\nFile %s \
Xnot archived. Exiting.\n", dirf, filename);
X    remove_files (dir, filename, count, finbuf);
X    exit (1);
X  }
X  fprintf (fout, "%s %ld ", stripped_file, (pure_bin ? -count : count));
X  for (i = 0; i < count; i++)
X    fprintf (fout, "%ld ", filesizes[i]);
X  fprintf (fout, "%s%s%s\n", dir, (text[0] != EOS ? " " : ""), text);
X  fclose (fout);
X  signal (SIGINT, SIG_DFL);
X  return 1;
X}
X
X/*
X  Remove all parts of the specified file from the specified directory.
X*/
X
Xvoid remove_files (char *dir, char *filename, long int count,
X		   struct stat finbuf)
X{
X  char file[10240];
X  char fout[10240];
X  struct stat foutbuf;
X  char yn;
X  BOOLEAN split = (count > 1 ? 1 : 0);
X
X  if (!split) {
X    sprintf (fout, "%s/%s", dir, filename);
X    syscom ("uncompress %s.Z  > /dev/null 2>&1 < /dev/null", fout);
X    if (stat (fout, &foutbuf)) {
X      printf ("Input file %s may be the same as output file %s/%s.\n\
XRemove %s/%s [y]? ",
X	      filename, dir, filename, dir, filename);
X      fflush (stdout);
X      scanf ("%c", &yn);
X      if (yn == 'n' || yn == 'N')
X        return;
X    }
X    if ((finbuf.st_ino == foutbuf.st_ino) &&
X	(finbuf.st_dev == foutbuf.st_dev)) {
X      printf ("\t- output file matches input: not removed\n");
X      return;
X    }
X  }
X  printf ("\t- removing output file(s)\n");
X  while (count) {
X    RESET (file);
X    if (split)
X      sprintf (file, "%s/%s%d", dir, filename, count);
X    else
X      sprintf (file, "%s/%s", dir, filename);
X    unlink (file);
X    strcat (file, ".Z");
X    unlink (file);
X    --count;
X  }
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "farch {[-n] [-b | -B] [-s size] \
X[-d dir] [-p password] [-D 'description'] [-t file]} | {<-r>} \
X[-a archive | path-to-archive] filename(s)\n");
X  fprintf (stderr, "-n: do not split input file(s)\n\
X-b: input files are binary: uuencode (on if -t specified)\n\
X-B input files are binary: do not uuencode and do not split\n\
X-s: maximum size of each output file (in kilobytes) -- default %ld\n\
X-d: actual directory to put output file(s)\n    -- default %s/%s\n\
X-p: password for a new private archive\n\
X-D: description of the file to go into the DIR file\n\
X-t: tar all input files into 'file' and archive that\n\
X-a: which archive index to update (subdir of %s)\n    -- \
Xdefault %s\n\
X-r: remove the specified file(s) from the archive\n",
X(long) DEFAULT_SIZE / 1024, ARCHIVE_DIR, DEFAULT_ARCHIVE, ARCHIVE_DIR,
XDEFAULT_ARCHIVE);
X  exit (3);
X}
X
X/*
X  Archive all files given in 'argv'.
X*/
X
Xvoid do_archive (int optind, int argc, char **argv, char *buf, char *fullpath,
X	         char *archive, char *dir, char *text, long int size,
X		 BOOLEAN bin, BOOLEAN uue, BOOLEAN split, BOOLEAN tar)
X{
X  FILE *fin, *fout, *flast;
X  long int l, count, nread, nchars, total, *filesizes = NULL;
X  char *leftovers = NULL, *s, *infile = NULL, filename[10240],
X    compressed[10240];
X  struct stat sbuf, finbuf;
X
X  while (optind < argc) { /* Split and archive each file */
X
X    s = argv[optind] + strlen (argv[optind]) - 1;
X    while (s != argv[optind] && *s == '/')  /* Remove trailing / */
X      *(s--) = EOS;
X
X    if ((fin = fopen (argv[optind], "r")) == NULL) { /* Open file */
X      fprintf (stderr, "Cannot open file %s for reading.\n", argv[optind]);
X      goto abort;
X    }
X
X    printf ("%s:\n", argv[optind]);
X    count = 1;
X    nchars = 0;
X
X    if (infile) /* free previous allocation */
X      free ((char *) infile);
X    infile = extract_filename (argv[optind]); /* get filename from path */
X    locase (infile);
X    l = strlen (infile);
X    if (l > 2 && infile [l - 2] == '.' && infile [l - 1] == 'z') {
X      infile [l - 2] = EOS;
X      l = strlen (argv[optind]);
X      argv[optind][l - 2] = EOS;
X      printf ("\t- uncompressing...\n");
X      syscom ("uncompress %s > /dev/null 2>&1 < /dev/null", argv[optind]);
X      fclose (fin);
X      fin = fopen (argv[optind], "r");
X    }
X
X    stat (argv[optind], &finbuf); /* Get size of input file */
X    if (bin && uue) /* uuencode */
X      uuencode (&fin, argv[optind], infile, &finbuf, tar);
X
X    if (!split || finbuf.st_size <= size) { /* Not splitting;may have to copy */
X      fclose (fin);
X      sprintf (filename, "%s%s%s", (strcmp (dir, "/") ? dir : ""),
X               ((strcmp (dir, infile) && strcmp (infile, "/")) ? "/" : ""),
X               infile);
X
X      if (bin) { /* Copy (possibly) uue_file to target directory */
X        if (uue && !stat (filename, &sbuf)) {
X	  fprintf (stderr, "Output file %s exists; cannot proceed with %s\n",
X		   filename, argv[optind]);
X	  unlink (uue_file);
X	  free ((char *) uue_file);
X	  goto abort;
X	}
X        if (uue && (fout = fopen (filename, "w")) == NULL) { /* Test */
X          fprintf (stderr, "Cannot write to directory %s; cannot \
Xproceed with %s\n",
X                   dir, argv[optind]);
X	  unlink (uue_file);
X	  free ((char *) uue_file);
X          goto abort;
X        }
X	if (uue)
X          fclose (fout),
X          syscom ("mv %s %s > /dev/null 2>&1", uue_file, filename);
X	else if (stat (filename, &sbuf)) { /* File does not exist in target dir */
X	  if ((fout = fopen (filename, "w")) == NULL) { /* Test */
X	    fprintf (stderr, "Cannot write to directory %s; cannot \
Xproceed with %s\n",
X		     dir, argv[optind]);
X	    goto abort;
X	  }
X	  fclose (fout);
X	  syscom ("cp %s %s > /dev/null 2>&1", argv[optind], filename);
X	}
X      }
X      else if (stat (filename, &sbuf)) { /* File does not exist in target dir */
X        if ((fout = fopen (filename, "w")) == NULL) {
X          fprintf (stderr, "Cannot write to directory %s; cannot \
Xproceed with %s\n",
X                   dir, argv[optind]);
X          goto abort;
X        }
X        fclose (fout);
X        syscom ("cp %s %s > /dev/null 2>&1", argv[optind], filename);
X      }
X
X      sprintf (compressed, "%s.Z", filename);
X      if (!stat (compressed, &sbuf)) {
X	fprintf (stderr, "Output file %s exists; cannot proceed with %s\n",
X		 compressed, argv[optind]);
X        if (bin && uue)
X	  unlink (uue_file),
X	  free ((char *) uue_file);
X	goto abort;
X      }
X
X      filesizes = (long int *) malloc (sizeof (long int));
X      *filesizes = (long int) finbuf.st_size;
X      syscom ("compress %s > /dev/null 2>&1 < /dev/null", filename);
X      if (update (fullpath, infile, 1, filesizes, dir, text, finbuf,bin & !uue))
X        printf ("\t- file not split\n\t- archived in %s\n\t- directory: %s\n",
X                archive, dir);
X      free ((long int *) filesizes);
X      filesizes = NULL;
X    }
X    else { /* Split file */
X      while (!feof (fin)) {
X        sprintf (filename, "%s%s%s%d", (strcmp (dir, "/") ? dir : ""),
X                 ((strcmp (dir, infile) && strcmp (infile, "/")) ? "/" : ""),
X                 infile, count);
X
X        if (!stat (filename, &sbuf)) {
X          fprintf (stderr, "Output file %s exists; cannot proceed with %s\n",
X                   filename, argv[optind]);
X	  if (bin)
X	    unlink (uue_file),
X	    free ((char *) uue_file);
X          fout = (FILE *) -1;
X          break;
X        }
X        if ((fout = fopen (filename, "w")) == NULL) {
X          fprintf (stderr, "Cannot write to directory %s; cannot proceed \
Xwith %s\n",
X                   dir, argv[optind]);
X          fout = (FILE *) -1;
X          break;
X        }
X
X        total = 0;  /* Do the actual splitting */
X        total += fwrite (leftovers, sizeof (char), nchars, fout);
X        nread = fread (buf, sizeof (char), size - nchars, fin);
X        if (nchars)
X          free ((char *) leftovers);
X        s = buf + nread - (nread > 0 ? 1 : 0);
X        nchars = 0;
X        while (s != buf && *s != '\n') /* Look for first \n from the end */
X          ++nchars,
X          --s;
X        if (s == buf) /* No new line found; write as is */
X          nchars = 0;
X        nread -= nchars;
X        leftovers = (char *) malloc (nchars * sizeof (char));
X        strncpy (leftovers, ++s, nchars); /* Copy anything after \n */
X        total += fwrite (buf, sizeof (char), nread, fout);
X        fclose (fout);
X
X        if (total == 0)
X          unlink (filename);
X        else { /* OK, compress */
X          sprintf (compressed, "%s.Z", filename);
X          if (!stat (compressed, &sbuf)) {
X            fprintf (stderr, "Output file %s exists; cannot proceed with %s\n",
X                     compressed, argv[optind]);
X	    if (bin)
X	      unlink (uue_file),
X	      free ((char *) uue_file);
X	    unlink (filename);
X            goto abort;
X          }
X	  stat (filename, &sbuf);
X	  filesizes = (long int *)
X	    (filesizes ?
X	      (realloc ((long int *) filesizes, count * sizeof (long int))) :
X	      (malloc (count * sizeof (long int))));
X	  *(filesizes + (count - 1)) = (long int) sbuf.st_size;
X          syscom ("compress %s > /dev/null 2>&1 < /dev/null", filename),
X	  ++count; /* Always one ahead */
X	}
X      }
X
X      fclose (fin);
X      if (bin)
X        unlink (uue_file),
X	free ((char *) uue_file);
X      if (nchars) /* write any leftovers */
X        flast = fopen (filename, "a"),
X        fwrite (leftovers, sizeof (char), nchars, flast),
X        fclose (flast),
X        free ((char *) leftovers),
X	stat (filename, &sbuf),
X	*(filesizes + (count - 2)) = (long int) sbuf.st_size;
X
X      if (fout != (FILE *) -1) { /* Splitting successful, update DIR file */
X        if (update (fullpath, infile, count - 1, filesizes, dir, text, finbuf,
X	    bin & !uue))
X          printf ("\t- file split in %ld parts\n\t- archived in %s\n\t\
X- directory: %s\n",
X                  (long) count - 1, archive, dir);
X	free ((long int *) filesizes);
X	filesizes = NULL;
X      }
X    }
X    abort:
X    ++optind;
X  }
X}
X
X/*
X  Get archive to update.
X*/
X
Xint get_archive (char **archive, char *fullpath, char *password,
X		 BOOLEAN remove_file)
X{
X  FILE *master;
X  char indexf[10240], line[10240], cur_archive[10240], arch[10240], dirf[10240],
X  error [10240], *slash;
X  char original[10240];
X  BOOLEAN found = FALSE;
X
X  if (!*archive)
X    *archive = DEFAULT_ARCHIVE;
X  strcpy (cur_archive, (char *) *archive);
X  strcpy (original, *archive);
X  if ((slash = strchr (cur_archive, '/')))
X    *slash = EOS;
X  sprintf (indexf, "%s/%s/%s", ARCHIVE_DIR, DEFAULT_ARCHIVE, INDEX);
X  while (*archive[0] != EOS) { /* Check all archives specified */
X    if ((master = fopen (indexf, "r")) == NULL) {
X      fprintf (stderr, "Sorry, no master index found.\n");
X      return 0;
X    }
X    found = FALSE;
X    while (!feof (master)) { /* Look at the current index for fullpath */
X      indexf[0] = arch[0] = RESET (line);
X      fgets (line, MAX_LINE - 2, master);
X      if (line[0] != EOS) {
X        sscanf (line, "%s %s\n", arch, indexf);
X        locase (arch);
X        if (!strcmp (arch, cur_archive)) {
X          found = TRUE;
X          break;
X        }
X      }
X    }
X    fclose (master);
X    if (!found && remove_file) {
X      fprintf (stderr, "%s: not a valid archive or path.\n", cur_archive);
X      return 0;
X    }
X    sprintf (dirf, "%s/%s/%s", ARCHIVE_DIR, original, DIRF);
X    if (!found) { 
X      sprintf (dirf, "%s/%s/%s", ARCHIVE_DIR, original, DIRF);
X      printf ("New archive %s; creating all directories and necessary files\n",
X	      original);
X      if (!make_indexes (dirf, original, password, error, "077")) {
X	fprintf (stderr, "%s", error);
X	return 0;
X      }
X      sprintf (indexf, "%s/%s", ARCHIVE_DIR, original);
X    }
X    if ((slash = strchr (*archive, '/')))
X      sprintf (*archive, "%s", slash + 1); /* Move down the path */
X    else
X      sprintf (*archive, "%s", *archive + strlen (cur_archive));
X    strcpy (cur_archive, *archive);
X    if ((slash = strchr (cur_archive, '/')))
X      *slash = EOS;
X    if (cur_archive[0] != EOS)
X      sprintf (indexf, "%s/%s", indexf, INDEX);
X  }
X  strcpy (fullpath, indexf);
X  strcpy (*archive, original);
X  return 1;
X}
X
X/*
X  Get output directory.
X*/
X
Xint get_dir (char *newdir, char **dir, char *fullpath)
X{
X  char *s;
X  char msg [MAX_LINE];
X  struct stat sbuf;
X
X  free ((char *) *dir);
X  if (!newdir)
X    *dir = (char *) malloc ((strlen (fullpath) + 1) * sizeof (char)),
X    sprintf (*dir, "%s", fullpath);
X  else {
X    *dir = (char *) malloc ((strlen (newdir) + 1) * sizeof (char));
X    sprintf (*dir, "%s", newdir);
X    s = *dir + strlen (*dir) - 1;
X    while (s != *dir && *s == '/')
X      *(s--) = EOS;
X  }
X  if (stat (*dir, &sbuf)) {
X    if (!mkdir1 (*dir, msg, "077")) {
X      fprintf (stderr, "Unable to create new directory %s\n%s", *dir, msg);
X      return 0;
X    }
X    printf ("Creating new directory %s\n", *dir);
X  }
X  return 1;
X}
X
X/*
X  Required to avoid undefined symbols.
X*/
X
Xint gexit (int exitcode)
X{
X  exit (exitcode);
X}
*-*-END-of-src/farch.c-*-*
echo x - src/fio.c
sed 's/^X//' >src/fio.c <<'*-*-END-of-src/fio.c-*-*'
X#include <stdio.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <fcntl.h>
X#include <errno.h>
X# ifdef unknown_port
Xextern int errno;
X# endif
X#if !defined (stellar) && !defined (unknown_port)
X# include <time.h>
X#endif
X#include <sys/time.h>
X#if !defined (__NeXT__) && !defined (stardent) && !defined (unknown_port)
X# include <utime.h>      /* not in NeXT OS 2.1; Under 3.0, included by sys/types.h */
X#else
X# include "next.h"
X#endif
X#include "defs.h"
X
X/*
X  Speed up the system by not forking off processes for things that
X  can be done with a syscall or two (or 50 for that matter).
X
X  Copyright 1993
X  Kenneth Lorber (keni@oasys.dt.navy.mil)
X  David Taylor Model Basin
X  Carderock Division, Naval Surface Warfare Center
X
X  This code may be freely redistributed.
X
X  [tasos: the code has been expanded to use the ULISTSERVER_UMASK environment
X  variable, fixed spacing to match the rest of the system and some function
X  prototyping problems; also altered syntax/use of utime() for NeXT hosts,
X  and removed const declarations which are not removed by the unproto package,
X  plus some bug fixing as well]
X*/
X
X#ifdef __STDC__
X# include <stdarg.h>
Xchar *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xchar *tsprintf ();
X#endif
X
Xextern char *extract_filename (char *);
Xextern void shrink (char *);
Xextern int otoi (char *);
Xextern char *getenv ();
X
Xtypedef enum append { NOAPPEND, APPEND } _append;
Xstatic int echo_internal (char *, char *, _append);
Xstatic int cat_internal (char *, char *, _append);
Xstatic int log_warn (char *);
X
Xint echo (char *, char *);
Xint echo_append (char *, char *);
Xint mv (char *, char *);
Xint cp (char *, char *);
Xint cat (char *, char *);
Xint cat_append (char *, char *);
Xint touch (char *);
X
Xint echo (char *text, char *file)
X{
X  return echo_internal (text, file, NOAPPEND);
X}
X
Xint echo_append (char *text, char *file)
X{
X  return echo_internal (text, file, APPEND);
X}
X
Xstatic int echo_internal (char *text, char *file, _append append)
X{
X  int d;
X  long int len;
X  char *mask;
X
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X  if (append == APPEND)
X    d = open (file, O_CREAT|O_APPEND|O_WRONLY, 0666 & (0666 ^ otoi (mask)));
X  else
X    d = open (file, O_CREAT|O_TRUNC|O_WRONLY, 0666 & (0666 ^ otoi (mask)));
X  if (d == -1) {
X    log_warn (tsprintf ("echo: open: %s", file));
X    return 1;
X  }
X
X  len = strlen (text);
X  if (len != write (d, text, len))
X    goto write_error;
X  if (1 != write (d, "\n", 1)) {
X  write_error:
X    close (d);
X    log_warn (tsprintf ("echo: write: %s", file));
X    return 1;
X  }
X	
X  if (close (d)) {
X    log_warn (tsprintf ("echo: close: %s", file));
X    return 1;
X  }
X  return 0;
X}
X
Xint mv (char *oldfile, char *newfile)
X{
X  (void) unlink (newfile);
X  errno = 0;
X  if (link (oldfile, newfile)) {
X    if (errno == EXDEV) {
X      if (cp (oldfile, newfile))
X	return 1; /* can't copy either */
X    }
X    else {
X      log_warn (tsprintf ("mv: %s %s", oldfile, newfile));
X      return 0;
X    }
X  }
X  unlink (oldfile);
X  return 0;
X}
X
Xint cp (char *oldfile, char *newfile)
X{
X  struct stat oldstat, newstat;
X  int oldfd, newfd;
X  char *newname, *buffer, *mask, buf[256];
X  long int size;
X
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X  oldfd = open (oldfile, O_RDONLY, 0);
X  if (oldfd < 0) {
X    log_warn (tsprintf ("cp: open: %s", oldfile));
X    return 1;
X  }
X  if (fstat (oldfd, &oldstat)) {
X    close (oldfd);
X    log_warn (tsprintf ("cp: fstat: %s", oldfile));
X    return 1;
X  }
X
X  newname = newfile;
X  if (!stat (newfile, &newstat)) {
X    if (newstat.st_mode & S_IFDIR) {
X      char *s;
X      sprintf (buf, "%s/%s", newfile, (s = extract_filename (oldfile)));
X      newname = buf;
X      free ((char *) s);
X    }
X  }
X
X  newfd = open (newname, O_WRONLY|O_TRUNC|O_CREAT,
X		oldstat.st_mode & (0666 ^ otoi (mask)));
X  if (newfd < 0) {
X    close (oldfd);
X    log_warn (tsprintf ("cp: open: %s", newname));
X    return 1;
X  }
X
X  buffer = (char *) malloc (65536 * sizeof (char));
X  while ((size = read (oldfd, buffer, 65536)) > 0) {
X    if (size != write (newfd, buffer, size)) {
X      free ((char *) buffer);
X      close (oldfd);
X      close (newfd);
X      log_warn (tsprintf ("cp: write %s", newname));
X      return 1;
X    }
X  }
X  free ((char *) buffer);
X  close (oldfd);
X  close (newfd);
X  if (size < 0) {
X    log_warn (tsprintf ("cp: read: %s", oldfile));
X    return 1;
X  }
X  return 0;
X}
X
Xint cat (char *srcfile, char *dstfile)
X{
X  return cat_internal (srcfile, dstfile, NOAPPEND);
X}
X
Xint cat_append (char *srcfile, char *dstfile)
X{
X  return cat_internal (srcfile, dstfile, APPEND);
X}
X
Xstatic int cat_internal (char *srcfile, char *dstfile, _append append)
X{
X  struct stat srcstat;
X  int srcfd, dstfd;
X  char *buffer, *mask;
X  long int size;
X
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X  srcfd = open (srcfile, O_RDONLY, 0);
X  if (srcfd < 0) {
X    log_warn (tsprintf ("cat: open: %s", srcfile));
X    return 1;
X  }
X  if (fstat (srcfd, &srcstat)) {
X    close (srcfd);
X    log_warn (tsprintf ("cat: fstat: %s", srcfile));
X    return 1;
X  }
X
X  if (append == APPEND)
X    dstfd = open (dstfile, O_WRONLY|O_APPEND|O_CREAT,
X		  srcstat.st_mode & (0666 ^ otoi (mask)));
X  else
X    dstfd = open (dstfile, O_WRONLY|O_TRUNC|O_CREAT,
X		  srcstat.st_mode & (0666 ^ otoi (mask)));
X  if (dstfd < 0) {
X    close (srcfd);
X    log_warn (tsprintf ("cat: open: %s", dstfile));
X    return 1;
X  }
X
X  buffer = (char *) malloc (65536 * sizeof (char));
X  while ((size = read (srcfd, buffer, 65536)) > 0) {
X    if (size != write (dstfd, buffer, size)) {
X      free ((char *) buffer);
X      close (srcfd);
X      close (dstfd);
X      log_warn (tsprintf ("cat: write: %s", dstfile));
X      return 1;
X    }
X  }
X  free ((char *) buffer);
X  close (srcfd);
X  close (dstfd);
X  if (size < 0) {
X    log_warn (tsprintf ("cat: read: %s", srcfile));
X    return 1;
X  }
X  return 0;
X}
X
Xint touch (char *file)
X{
X  char *mask;
X  int d;
X#ifdef __NeXT__
X  struct timeval tvp[2];
X#endif
X
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X  d = open (file, O_CREAT|O_EXCL, 0666 & (0666 ^ otoi (mask)));
X#ifdef __NeXT__
X  tvp[0].tv_sec = tvp[0].tv_usec = tvp[1].tv_sec = tvp[1].tv_usec = 0;
X#endif
X
X  if (d < 0) {
X    if (errno != EEXIST) {
X      log_warn (tsprintf ("touch: open: %s", file));
X      return 1;
X    }
X  }
X  else {
X    close (d);
X    return 0;
X  }
X#ifndef __NeXT__
X  if (utime (file, NULL)) {
X#else
X  if (utime (file, tvp)) {
X#endif
X    log_warn (tsprintf ("touch: utime: %s",file));
X    return 1;
X  }
X  return 0;
X}
X
X/* support routines */
X
X#ifdef __STDC__
Xchar *tsprintf (char *control, ...)
X#else
Xchar *tsprintf (control, va_alist)
Xchar *control;
Xva_dcl
X#endif
X{
X  static char command [10240];
X  va_list ap;
X
X#ifdef __STDC__
X  va_start (ap, control);
X#else
X  va_start (ap);
X#endif
X  RESET (command);
X  vsprintf (command, control, ap);
X  va_end (ap);
X  return command;
X}
X
Xstatic int log_warn (char *command)
X{
X  extern BOOLEAN tty_echo;
X  FILE *f;
X  struct tm *t;
X  long int time_is = 0;
X  int _errno = errno;
X
X  if ((f = fopen (WARNING, "a")) != NULL)
X    fprintf (f, "\nWARNING: System call errno %d: %s\n", _errno, command),
X    time (&time_is),
X    t = localtime (&time_is),
X    fprintf (f, "Time/Date: %2d:%.2d:%.2d, %2d/%.2d/%2d\n",
X	     t->tm_hour, t->tm_min, t->tm_sec, t->tm_mon + 1, t->tm_mday,
X	     t->tm_year),
X    fclose (f);
X  if (tty_echo)
X    printf ("\nWARNING: System call errno %d: %s\n", _errno, command);
X}
*-*-END-of-src/fio.c-*-*
echo x - src/fwin.c
sed 's/^X//' >src/fwin.c <<'*-*-END-of-src/fwin.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |				FILE WINDOW				     |
X  |									     |
X  |                             Version 1.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  Copy a window of the input (stdin) to output (stdout); the window is defined
X  as an offset from the beginning and a byte count. If no byte count is defined
X  then MAXINT is used.
X
X  COMMAND LINE OPTIONS:
X    -o: Set the offset from the beginning of the file (default 0).
X    -b: Set the byte count (default MAXINT).
X    -n: Pad with new line at the end if not there.
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifndef __NeXT__
X# ifndef unknown_port
X#  include <unistd.h>
X# endif
X# if !defined (sequent) && !defined (__convex__) && !defined (apollo) && \
X  !defined (unknown_port)
X#  include <values.h>
X# endif
X#else
X# include <libc.h>
X#endif
X
X#ifndef __NeXT__
Xextern long int atol (char *);
X#else
Xextern long int atol (const char *);
X#endif
X
X#define BUFFSIZ		8192
X#ifndef MAXINT
X# define MAXINT		0x7FFFFFFF	/* assumes 32-bit hardware */
X#endif
X
Xmain (int argc, char **argv)
X{
X  char *options = "o:b:n", last_byte;
X  long int offset = 0, bytes = MAXINT, bytes_read = 0, total_bytes = 0;
X  int pad_with_newline = 0, c;
X  char buf [BUFFSIZ];
X  extern char *optarg;
X
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X    case 'o':
X      if ((offset = atol (optarg)) < 0)
X	fprintf (stderr, "Negative offset (%d) not allowed.\n", offset),
X	exit (3);
X      break;
X    case 'b':
X      if ((bytes = atol (optarg)) < 0)
X	fprintf (stderr, "Negative number of bytes to output (%d) not allowed.\n", bytes),
X	exit (3);
X      break;
X    case 'n': pad_with_newline = 1; break;
X    default: 
X      printf ("Usage: %s [-n] [-o offset] [-b bytes]\n", argv[0]);
X      exit (3);
X    }
X
X  /* Skip to offset */
X  while ((bytes_read = read (fileno (stdin), buf, BUFFSIZ)) > 0) {
X    total_bytes += bytes_read;
X    if (total_bytes >= offset)
X      break;
X  }
X  if (total_bytes >= offset && bytes_read > 0) {
X    last_byte = 
X      buf[bytes_read - total_bytes + offset +
X	  (total_bytes - offset <= bytes ? total_bytes - offset : bytes) - 1];
X    total_bytes = 
X      write (fileno (stdout),
X	     &buf[bytes_read - total_bytes + offset],
X	     (total_bytes - offset <= bytes ? total_bytes - offset : bytes));
X    if (total_bytes < bytes)
X      while ((bytes_read = read (fileno (stdin), buf, BUFFSIZ)) > 0) {
X	total_bytes += bytes_read;
X	write (fileno (stdout), buf,
X	       (total_bytes <= bytes ?
X		bytes_read :
X		bytes_read - total_bytes + bytes));
X	last_byte = buf [(total_bytes <= bytes ? bytes_read :
X			  bytes_read - total_bytes + bytes) - 1];
X	if (total_bytes >= bytes)
X	  break;
X      }
X  }
X  if (pad_with_newline && last_byte != '\n')
X    write (fileno (stdout), "\n", 1);
X  fclose (stdout);
X  if (bytes_read > 0)
X    while (read (fileno (stdin), buf, BUFFSIZ) > 0);
X  exit (0);
X}
*-*-END-of-src/fwin.c-*-*
echo x - src/iul.c
sed 's/^X//' >src/iul.c <<'*-*-END-of-src/iul.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                   INTERACTIVE UNIX-LISTSERVER CLIENT		     |
X  |									     |
X  |				  Version 1.0				     |
X  |									     |
X  |                (or, when Computer Science gets to you)                   |
X  |									     |
X  |		       Written by Anastasios Kotsikonas                      |
X  |		    	      (tasos@cs.bu.edu)			     	     |
X  |									     |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.		     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  This program is to be used for connecting to an interactive UNIX ListServer
X  server (version 6.0 and up). The following files are required:
X
X  iul.c (this file)
X  iul.h
X  iulp.h (definition of the Interactive UNIX ListServer Protocol)
X  makefile
X
X  Usage: iul [-v] [-t timeout] [-b buffer-size] <host> [port]
X
X  To connect to a host you may specify its name or IP address. The default
X  port is 372; another port may be specified as an extra argument. To echo
X  server responses to client requests, use the -v flag.
X
X  The default time out for a server response is 180 seconds; to reset
X  use the -t flag. The default socket buffer size is 8K; to reset use
X  the -b flag (the argument specifies kilobytes).
X
X  This client is to be used as a model and better ones can certainly be
X  written.
X
X  SCO UNIX system should compile with -Dsco.
X
X  Refer to the man page for more information on how to use this program.
X*/
X
X#include <signal.h>
X#include "defs.h"
X#undef	 NSIG
X#ifdef GO_INTERACTIVE
X# include <stdio.h>
X# include <sys/types.h>
X# include <string.h>
X# if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X# endif
X# ifndef unknown_port
X#  ifndef __NeXT__
X#   include <unistd.h>
X#  else
X#   include <libc.h>
X#  endif
X# endif
X# if defined (stardent) || defined (stellar) || defined (titan)
X#  include <rpc/types.h>
X# endif
X# include <sys/stat.h>
X# include <fcntl.h>
X# include <errno.h>
X# ifdef unknown_port
Xextern int errno;
X# endif
X# include <sys/socket.h>
X# include <netinet/in.h>
X# include <netdb.h>
X# include <sys/file.h>
X# include <sys/ioctl.h>
X# if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X#  include <sys/stropts.h>
X# endif
X# if (defined (sco) || defined (M_XENIX) || defined (M_UNIX)) && \
X  defined (HAVE_SELECT_H)
X#  include <sys/times.h>
X# else
X#  include <sys/time.h>
X# endif
X# ifdef HAVE_SELECT_H
X#  include <sys/select.h>
X# endif
X# ifdef HAVE_ULIMIT_H
X#  include <ulimit.h>
X# endif
X# include "iul.h"
X
X# ifndef UL_GDESLIM
X#  define UL_GDESLIM	4
X# endif
X# ifndef FD_SET          /* for 4.2BSD */
X#  define FD_SETSIZE      (sizeof(fd_set) * 8)
X#  define FD_SET(n, p)    (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
X#  define FD_CLR(n, p)    (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
X#  define FD_ISSET(n, p)  (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
X#  define FD_ZERO(p)      memset ((char *)(p), '\0', sizeof(*(p)))
X# endif
X
X# ifndef __NeXT__
Xextern	long int atoi (char *);
X# else
Xextern	int atoi (const char *);
X# endif
Xextern  int optind;
X# if !defined (hpux) && !defined (__hpux)
Xextern  int getopt (int, char **, char *);
X# endif
X
Xint      main (int, char **);
Xint	 server_response (int);
Xint      build_tcp_connection (char *, int);
Xint	 sighandle (int);
Xint	 urg_data (int);
Xlong int read_from_fd (int, long int, FILE *);
Xlong int write_to_fd (int, char *, long int);
Xint	 open_file (char *, int, int);
Xint 	 check_server_response (int, char *);
Xint	 check_for_redirection (char *, char *);
XFILE     *check_for_pipe (char *);
X
Xlong int nbytes, sock_fd, buffsiz = BUFFSIZ;
Xint  to_file, prompt_len, verbose, check_sio, response_timeout = TIMEOUT,
X     fd = -1, broken_pipe = 0, sigint = 1;
Xchar args [256];
XFILE *pipefp = NULL;
X#endif
X
X/*
X  Main function. Connect to server, issue requests and receive replies.
X
X  Returns: 0 on success, 1 on command line option error, or an IULP
X	   command if the transaction was terminated normally.
X*/
X
Xmain (int argc, char **argv)
X{
X#ifndef GO_INTERACTIVE
X  printf ("%s not functional. Try compiling archives/iul/iul.c\n", argv[0]);
X#else
X  char *options = "vt:b:";
X  char *buf, *tmp, infile [256], wbuf [256], version [80];
X  int cmd, i, port, timeout, sprompt_len, c, rfd, bytes_alloced = 0;
X  struct stat stat_buf;
X  extern char *optarg;
X 
X  port = PORT;
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case 'v': verbose = 1; break;
X      case 't':
X	if ((response_timeout = atoi (optarg)) < 1) {
X	  printf ("-t %d\nBe real.\n", response_timeout);
X	  return 1;
X	}
X	break;
X      case 'b':
X	if ((buffsiz = atol (optarg) * 1024) < 1024) {
X	  printf ("-b %ld\nBe real.\n", buffsiz);
X	  return 1;
X	}
X	break;
X      case '?':
X      default:
X	fprintf (stderr, "Usage: %s [-v] [-t timeout] [-b buffer-size] \
X<host> [port]\n", argv[0]);
X	return 1;
X    }
X
X  if (optind == argc) {
X    fprintf (stderr, "Usage: %s [-v] [-t timeout] [-b buffer-size] \
X<host> [port]\n", argv[0]);
X    return 1;
X  }
X  else if (optind + 1 < argc)
X    port = atoi (argv [optind + 1]);
X# ifdef SIGIO
X  signal (SIGIO, (void (*)()) urg_data);
X# endif
X# ifdef SIGURG
X  signal (SIGURG, (void (*)()) urg_data);
X# endif
X  if ((sock_fd = build_tcp_connection (argv[optind], port)) < 0)
X    return SYS_ERROR;
X  if ((cmd = server_response (sock_fd)) != CONNECT) {
X    PRINTF (cmd, "Handshake failed\n");
X    printf ("Not an interactive UNIX ListServer.\n");
X    return cmd;
X  }
X
X  for (i = SIGHUP; i <= SIGPIPE; i++)
X    signal (i, (void (*)()) sighandle);
X
X  if (check_server_response (cmd, args)) goto abort;
X  timeout = nbytes;
X  sscanf (args, "%s %d %d\n", version, &prompt_len, &sprompt_len);
X
X  if ((cmd = server_response (sock_fd)) < 0) goto abort; /* Get greeting */
X  check_server_response (cmd, args);
X  if (cmd != CONN_CLOSED && cmd != SERVER_BUSY && cmd != PEER_UNAVAIL &&
X      cmd != CONN_ABORTED && cmd != SYS_ERROR && cmd != CONN_TIMEOUT)
X    printf ("\nHit ^\\ (control-backslash) to abort at any time.\n");
X  if (read_from_fd (sock_fd, nbytes, NULL) < 0) goto abort;
X  if (cmd == CONN_CLOSED || cmd == SERVER_BUSY || cmd == PEER_UNAVAIL ||
X      cmd == CONN_ABORTED || cmd == SYS_ERROR || cmd == CONN_TIMEOUT)
X    goto abort;
X  bytes_alloced = 256;
X  if ((buf = (char *) malloc (bytes_alloced * sizeof (char))) == NULL) {
X    printf ("FATAL: malloc() failed\n");
X    goto abort;
X  }
X  check_sio = 1;
X  fgets (buf, bytes_alloced - 1, stdin);		/* Put email address */
X  if (strlen (buf) == 0)
X    printf ("\n"),
X    strcpy (buf, "\n");
X  check_sio = 0;
X  if (write_to_fd (sock_fd, buf, strlen (buf)) < 0) goto abort;
X  if ((cmd = server_response (sock_fd)) < 0) /* See if "Password: " follows */
X    goto abort;
X  if (check_server_response (cmd, args)) goto abort;
X  if (cmd == PASSWORD_REQUIRED) {
X    RESET (buf);
X    check_sio = 1;
X    fgets (buf, 255, stdin);
X    if (strlen (buf) == 0)
X      printf ("\n"),
X      strcpy (buf, "\n");
X    check_sio = 0;
X    if (write_to_fd (sock_fd, buf, strlen (buf)) < 0)
X      goto abort;
X  }
X  if (cmd == PEER_UNAVAIL) {
X    read_from_fd (sock_fd, nbytes, NULL);
X    goto abort;
X  }
X
X  sigint = 0;
X  RESET (buf);
X  while (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT &&
X	 cmd != PEER_UNAVAIL) {
X    if ((cmd = server_response (sock_fd)) < 0) break;
X    if (check_server_response (cmd, args)) break;
X    if (cmd == TEST_FILE_PERMISSIONS) continue;
X    if (cmd != CONN_ABORTED && cmd != CONN_TIMEOUT)
X      if (read_from_fd (sock_fd, nbytes, (cmd != MESSAGE ? pipefp : NULL)) < 0)
X	break;
X    broken_pipe = 0;
X    if (cmd == MESSAGE) continue;
X    if (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT &&		cmd != PEER_UNAVAIL) {
X      check_sio = 1;
X      if (pipefp)
X	strcpy (buf, "\n");
X      if (buf [0] == EOS) {
X        RESET (infile);
X        fgets (buf, bytes_alloced - 1, stdin);
X        if (strlen (buf) == 0)    /* EOF */
X	  printf ("\n"),
X	  strcpy (buf, "\n");
X        if ((rfd = check_for_redirection (buf, infile)) < 0)
X	  strcpy (buf, "\n");
X        if (rfd >= 0 && infile [0] != EOS) { /* Input redirection */
X	  fstat (rfd, &stat_buf);
X	  if (!(stat_buf.st_mode & S_IFREG)) {
X	    printf ("%s: Not a regular file\n", infile);
X	    strcpy (buf, "\n");
X	    goto skip;
X	  }
X	  if (stat_buf.st_size + strlen (buf) + 2 > bytes_alloced) {
X	    bytes_alloced = stat_buf.st_size + strlen (buf) + 2;
X	    if ((buf = (char *) realloc (buf, bytes_alloced * sizeof (char))) ==
X	        NULL) {
X	      printf ("FATAL: realloc() failed\n");
X	      break;
X	    }
X	  }
X	  i = strlen (buf);
X	  if (read (rfd, &buf [strlen (buf)], stat_buf.st_size) <
X	      stat_buf.st_size) {
X	    printf ("FATAL: Failed to read all of the input file\n");
X	    break;
X	  }
X	  buf [i + stat_buf.st_size] = EOS;
X	  if (buf [i + stat_buf.st_size - 1] != '\n')
X	    strcat (&buf [i + stat_buf.st_size], "\n");
X	  close (rfd);
X        }
X      }
X      skip:
X      strncpy (wbuf, buf, 254);
X      tmp = strchr (wbuf, '\n');
X      if (!tmp)
X	wbuf [254] = '\n',
X	wbuf [255] = EOS;
X      else
X	*(tmp + 1) = EOS;
X      if (pipefp)
X	pclose (pipefp);
X      check_sio = 0;
X      pipefp = check_for_pipe (wbuf);
X      if (write_to_fd (sock_fd, wbuf, strlen (wbuf)) < 0) break;
X      sprintf (buf, "%s", strchr (buf, '\n') + 1);
X    }
X  }
X
X  abort:
X  if (pipefp)
X    pclose (pipefp);
X  close (sock_fd);
X  printf ("Connection closed.\n");
X  if (buf)
X    free ((char *) buf);
X  if (cmd == CONN_CLOSED) 
X    return 0;
X  return cmd;
X#endif
X}
X
X#ifdef GO_INTERACTIVE
X/*
X  Get server response. Store in 'nbytes' the number of bytes in the actual
X  message that follows, and in 'args' the (optional) file name.
X
X  Returns: the server reply code, CONN_TIMEOUT, CONN_ABORTED, or -1 on 
X  protocol errors.
X*/
X
Xint server_response (int sock_fd)
X{
X  char buf [256];
X  int cmd = -1, i, bytes_to_read = 4, bytes_read = 0, err, l = sizeof (int);
X  long int time_started;
X
X  time_started = time (0);
X  memset (buf, EOS, 256);
X  while (bytes_to_read) {
X    errno = 0;
X    /* Get server command */
X    bytes_read = read (sock_fd, &buf [4 - bytes_to_read], bytes_to_read);
X    if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	errno == ECONNREFUSED) {
X      nbytes = 0;
X      return CONN_ABORTED;
X    }
X    if (!getsockopt (sock_fd, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l)){
X      if (err || buf [4 - bytes_to_read] == EOF) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X    }
X    else
X      perror ("getsockopt() error");
X    if (errno || bytes_read < 1)
X      if ((time (0) - time_started) > response_timeout) {
X	nbytes = 0;
X	return CONN_TIMEOUT;
X      }
X    if (bytes_read > 0)
X      bytes_to_read -= bytes_read;
X  }
X  buf[3] = EOS;
X  cmd = atoi (buf);
X  i = 0;
X  do {		/* Get # of bytes in actual message */
X    again1:
X    if (read (sock_fd, &buf[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	  errno == ECONNREFUSED) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X	if ((time (0) - time_started) > response_timeout) {
X	  nbytes = 0;
X	  return CONN_TIMEOUT;
X	}
X	goto again1;
X      }
X      perror ("Protocol error in control string");
X      return -1;
X    }
X    i++;
X  } while (buf[i - 1] != ' ');
X
X  buf[i - 1] = EOS;
X  nbytes = atoi (buf);
X  RESET (args);
X  i = 0;
X  do {		/* Get filename (optional) */
X    again2:
X    if (read (sock_fd, &args[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X          errno == ECONNREFUSED)
X        return CONN_ABORTED;
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X        if ((time (0) - time_started) > response_timeout) {
X          nbytes = 0;
X          return CONN_TIMEOUT;
X        }
X        goto again2;
X      }
X      perror ("Protocol error in control string");
X      return -1;
X    }
X    ++i;
X  } while (args[i - 1] != '\n');
X  args[i - 1]= EOS;
X  return cmd;
X}
X
X/*
X  Establish connection with unix-listserver. The socket is marked as
X  non-blocking.
X
X  Returns: the socket file descriptor, or -1 on error.
X*/
X
Xint build_tcp_connection (char *host, int port)
X{
X  int sock_fd, sendbuf = buffsiz, recvbuf = buffsiz, value = 1, nfds, naddr = 0;
X  struct sockaddr_in sin;
X  struct hostent *hostentry;
X  struct timeval timeout;
X  fd_set readfds, writefds;
X 
X  timeout.tv_sec = timeout.tv_usec = 0;
X  if (!(hostentry = gethostbyname (host))) { /* Host name failed; try IP */
X    sin.sin_addr.s_addr = inet_addr (host);
X    if (! (hostentry = gethostbyaddr ((char *) &sin.sin_addr,
X				      sizeof (struct in_addr),
X				      AF_INET))) {
X      printf ("%s: No such host.\n", host);
X      return -1;
X    }
X  }
X
X  do {	/* Check all addresses */
X    if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X      perror ("Could not create socket");
X      return -1;
X    }
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		    sizeof (sendbuf)) < 0)
X      perror ("WARNING: Could not set send-buffer size: setsockopt() error");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		    sizeof (recvbuf)) < 0)
X      perror ("WARNING: Could not set receive-buffer size: setsockopt() error");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value,
X		    sizeof (value)) < 0)
X      perror ("WARNING: Cannot toggle keep-alive connections: setsockopt() error");
X
X# ifdef h_addr
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr_list[naddr++],
X	    hostentry->h_length);
X# else
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr,
X	    hostentry->h_length);
X# endif
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons (port);
X    memset (sin.sin_zero, EOS, sizeof (sin.sin_zero));
X    printf ("Trying %s ...\n", inet_ntoa (sin.sin_addr));
X    if (connect (sock_fd, (struct sockaddr *) &sin, 
X		 sizeof (struct sockaddr_in)) < 0) {
X      if (errno != EINPROGRESS) {
X# ifdef h_addr
X	if (!hostentry->h_addr_list[naddr]) {
X# endif	
X	  perror ("Could not connect to host");
X	  close (sock_fd);
X	  return -1;
X# ifdef h_addr
X	}
X	else {
X	  close (sock_fd);
X	  continue;
X	}
X# endif
X      }
X      FD_ZERO (&readfds);
X      FD_ZERO (&writefds);
X      do {
X	FD_SET (sock_fd, &readfds);
X	FD_SET (sock_fd, &writefds);
X	errno = 0;
X# if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X  defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X# else
X	nfds = ulimit (UL_GDESLIM);
X# endif
X	value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds, NULL,
X			&timeout);
X      } while (value == -1 && errno == EINTR);
X
X      if (value < 0) {
X	perror ("select() error");
X	close (sock_fd);
X	return -1;
X      }
X      break;	/* Successful connection */
X    }
X    else
X      break;
X  } while (007);
X# ifdef NONBLOCKING_IO
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | O_NDELAY)) < 0) {
X    perror ("Could not set non-blocking I/O");
X    close (sock_fd);
X    return -1;
X  }
X# endif
X# if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X  if (ioctl (sock_fd, I_SETSIG, S_INPUT) < 0) {
X    perror ("Cannot set SIGIO");
X    close (sock_fd);
X    return -1;
X  }
X# else
X#  ifdef F_SETOWN
X  if (fcntl (sock_fd, F_SETOWN, getpid()) < 0) {
X    perror ("Cannot assign socket to process group");
X    close (sock_fd);
X    return -1;
X  }
X#  elif defined (SIOCSPGRP)
X  value = -getpid();
X  if (ioctl (sock_fd, SIOCSPGRP, (char *) &value) < 0) {
X    perror ("Cannot assign socket to process group");
X    close (sock_fd);
X    return -1;
X  }
X#  else
X  perror ("Cannot assign socket to process group");
X  close (sock_fd);
X  return -1;
X#  endif
X#  ifdef FASYNC
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | FASYNC)) < 0) {
X    perror ("Cannot set asynchronous I/O for socket");
X    close (sock_fd);
X    return -1;
X  }
X#  elif defined (FIOASYNC)
X  value = 1;
X  if (ioctl (sock_fd, FIOASYNC, (char *) &value) < 0) {
X    perror ("Cannot set asynchronous I/O for socket");
X    close (sock_fd);
X    return -1;
X  }
X#  else
X  perror ("Cannot set asynchronous I/O for socket");
X  close (sock_fd);
X  return -1;
X#  endif
X# endif
X  return sock_fd;
X}
X
X/*
X  Handle signals; the client sends an '__abort__' command and exits; '__abort__'
X  causes the other end to shut down.
X*/
X
Xint sighandle (int sig)
X{
X  if (sig == SIGHUP) {
X    signal (sig, (void (*)()) sighandle);
X    return;
X  }
X  else if (sig == SIGPIPE) {
X    if (!broken_pipe)
X      printf ("Broken pipe\n");
X    broken_pipe = 1;
X    signal (sig, (void (*)()) sighandle);
X    return;
X  }
X  else if (sig == SIGINT) {
X    if (!sigint) {
X      signal (sig, (void (*)()) sighandle);
X      return;
X    }
X  }
X  check_sio = 0;
X  write_to_fd (sock_fd, "__abort__\n", 10);
X  close (sock_fd);
X  if (pipefp)
X    pclose (pipefp);
X  printf ("Connection closed.\n");
X  exit (sig);
X}
X
X/*
X  Urgent data from server; read it and exit. Urgent data is received
X  when a connection is timed out or when the server shuts down.
X*/
X
Xint urg_data (int sig)
X{
X  int cmd;
X
X  if (!check_sio) {
X    signal (sig, (void (*)()) urg_data);
X    return;
X  }
X  signal (sig, SIG_IGN);
X  if ((cmd = server_response (sock_fd)) >= 0)
X    if (cmd == CONN_ABORTED || cmd == CONN_TIMEOUT || cmd == CONN_CLOSED)
X      check_server_response (cmd, args),
X      read_from_fd (sock_fd, nbytes, NULL);
X  shutdown (sock_fd, 2);
X  kill (0, SIGHUP);
X  if (pipefp)
X    pclose (pipefp);
X  printf ("Connection closed.\n");
X  exit (cmd);
X}
X
X/*
X  Read from a socket and either echo to tty or save to a file.
X
X  Returns: the actual number of bytes read on succes, or the negative of
X  that number (or -1) on error.
X*/
X
Xlong int read_from_fd (int rfd, long int bytes_to_read, FILE *pipefp)
X{
X  long int bytes_read, total_bytes = 0, time_started;
X  int to_pipe = 1;
X  char *buf;
X
X  time_started = time (0);
X  if (bytes_to_read <= 0)
X    return 0;
X  if ((buf = (char *) malloc (buffsiz * sizeof (char))) == NULL) {
X    printf ("malloc() failed while attempting to allocate %ld bytes\n",
X	     buffsiz);
X    return -1;
X  }
X  errno = 0;
X  while ((bytes_read = read (rfd, buf, MIN (bytes_to_read, buffsiz))) <
X	 bytes_to_read) {
X    if (bytes_read < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN &&
X	errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      switch (errno) {
X	case EBADF: sprintf (error, "Bad file number"); break;
X	case EFAULT: sprintf (error, "Bad address"); break;
X	case EFBIG: sprintf (error, "File limit reached"); break;
X	case EINVAL: sprintf (error, "Negative seek pointer"); break;
X	case EIO: sprintf (error, "I/O error"); break;
X	case ENOSPC: sprintf (error, "No space left on device"); break;
X	case ENXIO: sprintf (error, "No such device or address"); break;
X	case ERANGE: sprintf (error, "Bytes to read (%d) out of \
Xrange", bytes_to_read); break;
X	default: sprintf (error, "Error number %d", errno);
X      }
X      fprintf (stderr, "%s\n", error);
X      free ((char *) buf);
X      return -1;
X    }
X    else if (!pipefp && (time (0) - time_started) > response_timeout) {
X      printf ("Server response timeout.\n");
X      free ((char *) buf);
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    if (bytes_read > 0) {
X      total_bytes += bytes_read;
X      bytes_to_read -= bytes_read;
X      if (to_file) {
X	if (bytes_to_read < prompt_len)
X	  write_to_fd (fileno (stdout),
X		       &buf[bytes_read - prompt_len + bytes_to_read],
X		       prompt_len - bytes_to_read),
X	  to_file = 0;
X	if (write_to_fd (fd, buf,
X			 (bytes_to_read < prompt_len ? 
X			  bytes_read - prompt_len + bytes_to_read :
X			  bytes_read)) < 0) {
X	  free ((char *) buf);
X	  return (total_bytes > 0 ? -total_bytes : -1);
X	}
X      }
X      else if (pipefp) {
X	if (to_pipe && !broken_pipe) {
X	  write_to_fd (fileno (pipefp), buf,
X		       (bytes_to_read < prompt_len ? 
X			bytes_read - prompt_len + bytes_to_read :
X			bytes_read));
X	  if (errno) {
X	    if (!broken_pipe && errno == EPIPE)
X	      printf ("Broken pipe\n");
X	    broken_pipe = 1;
X	    if (errno != EPIPE && errno != EIO
X#ifdef ERESTART
X		&& errno != ERESTART
X#endif
X		)
X	      printf ("errno %d\n", errno);
X	  }
X	}
X	if (bytes_to_read < prompt_len)
X	  to_pipe = 0;
X      }
X      else
X	write_to_fd (fileno (stdout), buf, bytes_read);
X    }
X    errno = 0;
X  }
X  if (bytes_read > 0) {
X    total_bytes += bytes_read;
X    if (to_file && bytes_read > prompt_len) {
X      write_to_fd (fileno (stdout), &buf [bytes_read - prompt_len], prompt_len);
X      if (write_to_fd (fd, buf, 
X		       (bytes_read > prompt_len ?
X			bytes_read - prompt_len : bytes_read)) < 0) {
X	free ((char *) buf);
X	return (total_bytes > 0 ? -total_bytes : -1);
X      }
X    }
X    else if (pipefp) {
X      if (!broken_pipe) {
X	write_to_fd (fileno (pipefp), buf,
X		     (bytes_read > prompt_len ? bytes_read - prompt_len
X		      : 0));
X	if (errno) {
X	  if (!broken_pipe && errno == EPIPE)
X	    printf ("Broken pipe\n");
X	  if (errno != EPIPE && errno != EIO
X#ifdef ERESTART
X	      && errno != ERESTART
X#endif
X	      )
X	    printf ("errno %d\n", errno);
X	}
X      }
X    }
X    else
X      write_to_fd (fileno (stdout), buf, bytes_read);
X  }
X  if (fd >= 0)
X    close (fd),
X    fd = -1;
X  to_file = 0;
X  free ((char *) buf);
X  fflush (stdout);
X  if (pipefp)
X    fflush (pipefp);
X  return total_bytes;
X}
X
X/*
X  Write to a file descriptor (socket, or regular file).
X
X  Returns: the actual number of bytes written on succes, or the negative of
X  that number (or -1) on error.
X*/
X
Xlong int write_to_fd (int wfd, char *buf, long int bytes_to_write)
X{
X  long int bytes_written, total_bytes = 0, bytes_requested = bytes_to_write;
X
X  if (bytes_to_write == 0)
X    return 0;
X  errno = 0;
X  while ((bytes_written = write (wfd, buf, bytes_to_write)) < bytes_to_write) {
X    if (bytes_written < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN
X	&& errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      switch (errno) {
X	case EBADF: sprintf (error, "Bad file number"); break;
X	case EFAULT: sprintf (error, "Bad address"); break;
X	case EFBIG: sprintf (error, "File limit reached"); break;
X	case EINVAL: sprintf (error, "Negative seek pointer"); break;
X	case EIO: sprintf (error, "I/O error"); break;
X	case ENOSPC: sprintf (error, "No space left on device"); break;
X	case ENXIO: sprintf (error, "No such device or address"); break;
X	case ERANGE: sprintf (error, "Bytes to write (%d) out of \
Xrange", bytes_to_write); break;
X	case EPIPE: sprintf (error, "Server disappeared"); break;
X	default: sprintf (error, "Error number %d", errno);
X      }
X      fprintf (stderr, "%s\n", error);
X      if (bytes_written > 0)
X	total_bytes += bytes_written;
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    if (bytes_written > 0)
X      bytes_to_write -= bytes_written,
X      total_bytes += bytes_written,
X      buf += bytes_written;
X    errno = 0;
X  }
X  return bytes_requested;
X}
X
X/*
X  Open a file for writing or appending. If 'test' is set, make sure
X  the file is writeable, notify the server and close the file; else prepare
X  for the transfer. When the server is inquiring about a file's write
X  permissions it sends zero bytes as the length of the message.
X
X  Returns: 0 on succes, -1 otherwise.
X*/
X
Xint open_file (char *file, int mode, int test)
X{
X  char msg [4];
X
X  if ((fd = open (file, O_CREAT | mode, 0600)) < 0)
X    to_file = 0,
X    printf ("%s: Permission denied.\n", args),
X    nbytes = prompt_len,
X    sprintf (msg, "%d", PERMISSION_DENIED);
X  else {
X    if (nbytes > 0)
X      to_file = 1,
X      printf ("Transferring %ld bytes to file %s...\n", nbytes - prompt_len,
X	      file);
X    else
X      close (fd),
X      sprintf (msg, "%d", OK);
X  }
X  if (test)
X    if (write_to_fd (sock_fd, msg, strlen (msg)) < 0)
X      return -1;
X  return 0;
X}
X
X/*
X  Identify the server's response and take appropriate action.
X
X  Returns: 0 on success, -1 otherwise.
X*/
X
Xint check_server_response (int cmd, char *file)
X{
X    switch (cmd) {
X    case OK:
X    case CONNECT:
X    case SYNTAX_ERROR:
X    case INVALID_REQ:
X    case PEER_UNAVAIL:
X    case BAD_ARCHIVE:
X    case RESTRICTED_REQ:
X    case NOT_OWNER:
X    case SYS_ERROR:
X    case SERVER_BUSY:
X    case PASSWORD_REQUIRED:
X    case PERMISSION_DENIED:
X    case MESSAGE:
X    case CONTINUED:
X    case MORE_INPUT_REQUIRED:
X    case CONN_ABORTED:
X    case CONN_TIMEOUT:
X    case CONN_CLOSED: GENERAL_RESPONSES (cmd); break;
X    case TEST_FILE_PERMISSIONS: PRINTF (TEST_FILE_PERMISSIONS,
X	 "Testing file permissions\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 1)) return -1;
X	 break;
X    case WRITE_TO_FILE_ASC: PRINTF (WRITE_TO_FILE_ASC,
X	 "Writing to file (ASCII)\n");
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case WRITE_TO_FILE_BIN: PRINTF (WRITE_TO_FILE_BIN,
X	 "Writing to file (BIN)\n");
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_ASC: PRINTF (APPEND_TO_FILE_ASC,
X	 "Appending to file (ASCII)\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_BIN: PRINTF (APPEND_TO_FILE_BIN,
X	 "Appending to file (BIN)\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    default: printf ("Protocol error: %d\n", cmd); return -1;
X  }
X  return 0;
X}
X
X/*
X  Check for input redirection, and if so check for for permissions too.
X
X  Returns: the opened file descriptor on succes, -1 on error.
X*/
X
Xint check_for_redirection (char *buf, char *infile)
X{
X  char *r, *s = strrchr (buf, EOS);
X  int fd = 0, nquote = 0;
X
X  while (s >= buf) {
X    if (!nquote) {
X      if (*s == '\'' && (s == buf || *(s - 1) != '\\')) /* Count single quotes */
X	nquote = 1;
X      else if (*s == '"' && (s == buf || *(s - 1) != '\\')) /* Count double quotes */
X	nquote = 2;
X      else if (*s == '<')
X	if (s != buf && *(s - 1) == '\\')
X	  sprintf (s - 1, "%s", s), /* Remove \ */
X	  --s;
X	else {
X	  RESET (infile);
X	  sscanf (s + 1, "%s", infile);
X	  if (infile [0] == EOS) {
X	    printf ("Invalid null input redirect\n");
X	    return -1;
X	  }
X	  if ((fd = open (infile, O_RDONLY)) < 0) {
X	    printf ("%s: No such file or inadequate permissions\n", infile);
X	    return -1;
X	  }
X	  r = s + 1; /* Remove < and file name */
X	  while (*r == ' ' || *r == '\t') ++r;
X	  while (*r != ' ' && *r != '\t' && *r != '\n') ++r;
X	  sprintf (s, "%s", r);
X	}
X    }
X    else if ((nquote == 1 && *s == '\'' &&
X	      (s != buf ? *(s - 1) != '\\' : 1)) ||
X	     (nquote == 2 && *s == '"' && 
X	      (s != buf ? *(s - 1) != '\\' : 1)))
X      nquote = 0;
X    --s;
X  }
X  if (nquote) {
X    printf ("Mismatched quotes\n");
X    strcpy (buf, "\n");
X    if (fd > 0)
X      close (fd);
X    return -1;
X  }
X  return fd;
X}
X
X/*
X  Check for UNIX pipe.
X
X  Returns: the open file pointer, or NULL.
X*/
X
XFILE *check_for_pipe (char *buf)
X{
X  char cmd [1024], *s = buf;
X  int nquote = 0;
X  FILE *pipe = NULL;
X
X  RESET (cmd);
X  while (*s != EOS) {
X    if (!nquote) {
X      if (*s == '\'' && (s == buf || *(s - 1) != '\\')) /* Count single quotes */
X	nquote = 1;
X      else if (*s == '"' && (s == buf || *(s - 1) != '\\')) /* Count double quotes */
X	nquote = 2;
X      else if (*s == '|' && cmd[0] == EOS)
X	if (s != buf && *(s - 1) == '\\')
X	  sprintf (s - 1, "%s", s), /* Remove \ */
X	  --s;
X	else {
X	  sscanf (s + 1, "%s", cmd);
X	  if (cmd [0] == EOS) {
X	    printf ("Invalid null pipe\n");
X	    strcpy (buf, "\n");
X	    return NULL;
X	  }
X	  strcpy (cmd, s + 1);
X	  strcpy (s, "\n");
X	  pipe = popen (cmd, "w");
X	}
X    }
X    else if ((nquote == 1 && *s == '\'' && *(s - 1) != '\\') ||
X	     (nquote == 2 && *s == '"' && *(s - 1) != '\\'))
X      nquote = 0;
X    ++s;
X  }
X  if (nquote)
X    printf ("Mismatched quotes\n"),
X    strcpy (buf, "\n");
X  return pipe;
X}
X#endif
*-*-END-of-src/iul.c-*-*
echo x - src/list.c
sed 's/^X//' >src/list.c <<'*-*-END-of-src/list.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |		    MAILING LIST MAIL-DISTRIBUTION PROGRAM		     |
X  |									     |
X  |				 Version 5.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |									     |
X  |		       Written by Anastasios Kotsikonas                      |
X  |		    	      (tasos@cs.bu.edu)			     	     |
X  |	                                                                     |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the author notice in the file defs.h;      |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header()       |
X  | and create_multi_recipient_header() in list.c and listserv.c.	     |
X  | By using this software you are bound by this agreement.		     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.				     |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  NOTE: Anything appearing in capital letters refers to #define's in the
X  header files provided.
X
X  PURPOSE: Create mailing lists. Members of the list send messages to
X  them, and each of these messages is forwarded to the rest of the members.
X  Any message(s) not from subscribers are returned to the original senders,
X  or can be forwarded to MANAGER. Messages from "undesired" senders can be
X  ignored by placing their email addresses in the IGNORED file (this would
X  usually include root and the list's login name to avoid infinite loops).
X  Distributed may be automatically archived.
X
X  OVERVIEW: A mailing list resides in a subdirectory of HOMEDIR/lists
X  whose name is the list's alias (in the aliases file) in capital letters.
X  When the program is killed or it abnormally dies, a message
X  is sent to MANAGER (if using UCB mail) along with a copy of the current 
X  report file (more on that below). Use the -1 flag when running the
X  program. The mailing list to be processed is given as argument to the
X  -L option in upper case. Any progress is reported to the list's REPORT_LIST
X  file and to the administrator's terminal. All messages sent to this list are
X  saved in the MBOX file. The program can be run as stand-alone, or in
X  conjunction with the other programs provided (start, serverd and listserv).
X  Please note that a line beginning with "From " and another one beginning
X  with "From: " must appear in the header of each message, and a blank line
X  must separate the header from the body of the message. Mailing lists
X  can be linked with peers and have access to news groups. A crash recovery
X  mechanism is built into the system, so that when the system is aborted
X  while a delivery is taking place, it will resume delivery from where
X  it left off.
X
X  COMMAND LINE OPTIONS:
X    -r: Restricted mail; since it is possible to have another distribution
X        list at some other site as a subscriber, then the following problem
X	arises: if a message is coming from a member of the other list, his/
X	her message was forwarded to the members of that list from the other
X	site; we do not want to send this message back to them, because they
X	will receive this message twice. Therefore, when this flag is on,
X	for every message received, its sender is checked against a list
X	of "restricted" mail addresses (a subset of the subscribers). If a
X	match is found, then mail is forwarded to the people listed in the
X	filename following this email address -- for the appropriate format
X	see below. If no match is found, then the message is forwarded to
X	to all of the subscribers. See also below.
X    -1: Execute only once; this is used when the program is running in
X        conjunction with listserv, in which case execution is interchanged
X        and controlled by the serverd program.
X    -e: Echo reports to the screen.
X    -s: Do not check for subscriptions.
X    -p: By default, replies to posted (to news) messages go to the list;
X	this option forces replies to be forwarded to the original author.
X    -P: By default, replies to distributed messages go to the list; this option
X	forces replies to be forwarded to the original author.
X    -m: Usually, each outgoing message has a single recipient. This switches
X        to multiple recipients -- the argument to it is the number of
X	multiple recipients to be included in this message.
X    -v: Display the version number of this package.
X    -f: Forward any message(s) from unsubscribed senders to the list's
X        owner. This way, any ordinary user account can be used as the list's
X        address.
X    -L: The argument following is the list name to process.
X    -D: Turn debug on. A transaction of the last email sent out is kept
X        in the files HOMEDIR/sent and HOMEDIR/received. This assumes
X        use of the 'system' mail method.
X    -M: list is moderated.  Incoming messages go to owner unless
X        they are from owner in which case they get posted.
X    -d: send out the digest even though it's not full
X    -i: send out a digest to one user (whose name is the argument)
X
X  DISCLAIMER: If for any reason during the use of this program, implied
X  or not, you happen to die, or suffer any injury of any kind (physical
X  or mental), I, Mr. Anastasios Kotsikonas, AM NOT RESPONSIBLE at all.
X  In fact, I AM NOT RESPONSIBLE FOR ANYTHING that may happen to you, or
X  your computer and operating system.
X
X  PLEASE: If you upgrade the code, send me a copy, and do not even attempt to
X  put your name for credit. Send to tasos@cs.bu.edu or tasos@bucsf.bu.edu.
X
X  EXIT CODES:
X    0: OK
X    1: Could not open or lock file
X    2: SIGINT signal
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X    9: Too many multiple recipients
X   10: Could not deliver mail
X   11: Malloc failed
X   12: Cannot fork
X   13: Socket connection problem
X   14: Semaphore error
X   15: Cannot setuid, setgid
X   16: Internal error
X
X  ENJOY!!!
X
X  Approximate algorithm:
X  {
X    If DIGEST_TMP file exists
X      send it to digest subscribers
X
X    If the -d flag is on
X      make a digest
X      send it to digest subscribers
X
X    Place a lock so that no other list program will access any files.
X    Read LIST_MAIL_FILE
X    If new message(s) have arrived then {
X      Lock LIST_MAIL_FILE so catmail can't append to it
X      Append messages to MBOX and truncate LIST_MAIL_FILE
X      Unlock LIST_MAIL_FILE
X
X      For each message do {
X        If the person is in the IGNORED file, go on to the next message.
X        If the person sending it is subscribed (listed in SUBSCRIBERS) then
X	  If the person does not acknowledge his/her message, he/she
X	    never receives his/her message back
X
X          If the -m flag is on then
X            If the article comes from someone who is not the owner
X              forward to the owner
X              go on to next message
X            Else
X              remove headers accumulated in passing to the moderator and back
X              figure out original sender and subject
X
X	  If the -r flag is on then
X	    check if the sender is listed in RESTRICTED
X	    If so then
X	      forward mail to all people listed in the file after the 
X	      restricted-sender's address
X	    Else
X	      distribute to all people in SUBSCRIBERS
X	  Else
X	    distribute to all people in SUBSCRIBERS, NEWSF (only to those
X	    newsgroups that are supposed to received messages) and PEERS
X	    according to the following:
X	      Email from regular SUBSCRIBERS is sent to NEWS and PEERS as well
X	      Email from news is sent to SUBSCRIBERS and PEERS
X	      Email from peers is sent to SUBSCRIBERS and NEWS
X	Else if it news feed (listed in NEWSF)
X	  distribute to SUBSCRIBERS
X        Else 
X          if -f specified, forward it to MANAGER; otherwise return the
X	  message to the sender.
X	Archive message is requested.
X      }
X      Remove mail files.
X    }
X    Repeat process after IDLE_TIME, or die if -1 specified.
X  }
X
X  Required files:
X    SUBSCRIBERS     <-- The list of subscribed people
X    ALIASES	    <-- Aliases of email addresses of subscribers, news & peers
X    NEWSF	    <-- List of news groups
X    PEERS	    <-- List of peer lists
X    RESTRICTED      <-- Addresses of senders whose messages
X                        require special handling (usually 
X		        addresses of other lists): mail is 
X			forwarded only to people found in the 
X			file after the sender's address; see below
X    IGNORED         <-- The list of undesired people
X    LIST_LOCK_FILE  <-- Lock file
X
X  Input files:
X    LIST_MAIL_FILE  <-- File where new messages go
X    MAIL_COPY       <-- Copy of this file (actual work file)
X    MSG_NO          <-- Current message count
X    DIGEST_NO       <-- Current digest count
X    SUBSCRIBERS
X    RESTRICTED
X    IGNORED
X
X  Output files:
X    MBOX            <-- A log of all messages sent to date
X    REPORT_LIST     <-- Progress report
X    HEADERS	    <-- A log of all emails sent (just the sender's address)
X    MSG             <-- Body of message (no header)
X    MSG_NO          <-- Write last message count
X    DIGEST_NO       <-- Write last digest count
X    MAILFORWARD     <-- Completed message (with header and a copy
X    			of MSG) to be forwarded
X
X  Format of the SUBSCRIBERS file:
X    One entry per line; each entry is the full email address of the subscriber
X    as it appears in the "From " field, followed by the word "ACK" (in which
X    case his/her message will be sent back to him/her as an acknowledgement)
X    "NOACK" (the opposite), POSTPONE (no mail will be sent until the
X    user changes mode again), or DIGEST (digests are periodically sent),
X    followed by a string that plays the role of a password, followed by either
X    YES or NO which play the role of the conceal attribute, followed by the
X    subscriber's name. Do not include any blank lines.
X
X  Format of the RESTRICTED file:
X    One entry per line; each entry is the full email address of the
X    subscriber, followed by a file name where email addresses of recipients
X    are listed (just like in the SUBSCRIBERS file).When mail arrives from a 
X    sender listed in the RESTRICTED file and the -r flag is on, then mail 
X    will not be forwarded to SUBSCRIBERS, but instead to the people 
X    listed in the file following the restricted-sender's address. Example:
X      tasos@bucsf.bu.edu  /grad/tasos/.recipients
X      tasos@cs.bu.edu     /grad/tasos/.otherrecipients
X    If the recipient file given is the word "NONE", then no one will receive
X    any messages. This is useful in the case that other distribution sites
X    are subscribers and do forward the message back to the sender, 
X    in which case we protect ourselves from multiple reception of the same
X    messages. 
X    For example, when two (or more) lists are mutual subscribers, and a 
X    message originated by someone in our list, this message, after being 
X    distributed locally, is sent to the other list; if the other site does
X    send messages back to the sender, this message will be forwarded to us
X    to be distributed again, something which is highly undesirable. 
X    This precludes that the other site sends messages identified only by
X    their original senders, something which is unlikely but possible.
X    In this case, we would put our list as one of the entries in the 
X    RESTRICTED file, with the word NONE next to it. Do not include 
X    any blank lines.
X
X  Format of the PEERS file:
X    One entry per line -- the email address of the peer list, followed
X    by its mail mode (NOACK), followed by the remote alias (how the peer list
X    is known in the remote host), followed by the email address of the
X    remote server that handles the remote peer list.
X
X  Format of the NEWSF file:
X    One entry per line -- the email address of the news group, followed
X    by its mail mode (POSTPONE or NOACK), followed by the news group's name.
X
X  Format of the IGNORED file:
X    One entry per line -- the email addresses of the people whose messages
X    are to be ignored. This is a good place to put root, sys and other
X    such undesired logins. See doc/server.nr for information.
X
X  Format of the ALIASES file:
X    One entry per line -- the new alias followed by the email address the
X    user is subscribed with. See doc/server.nr for information.
X
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#include <ctype.h>
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X  && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/stat.h>
X#if !defined (sequent) && !defined (__NeXT__) && !defined (__convex__) && \
X !defined (apollo) && !defined (i386) && !defined (unknown_port)
X# include <sys/termio.h>
X#endif
X#ifndef sun
X# include <sys/ioctl.h>
X#endif
X#include <fcntl.h>
X#include <signal.h>
X#include <time.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include "defs.h"
X#include "list.h"
X#include "struct.h"
X#include "global.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X#ifdef TCP_IP
X# include <sys/socket.h>
X# include <netdb.h>
X# include <netinet/in.h>
Xstruct	 in_addr localaddr;
X#else
Xchar	 *localaddr;
X#endif
X
X/* 
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xextern int  syscom ();
Xextern char *tsprintf ();
X#endif
X#ifndef __NeXT__
Xextern long int atoi (char *);
X#else
Xextern int atoi (const char *);
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern void report_progress (FILE *, char *, int);
Xextern void setup_string (char *, char *, char *);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern void extract_origin (char *);
Xextern void extract_address (char *);
Xextern int  get_list_id (char *, SYS *, int);
Xextern void clean_request (char *);
Xextern int  _getopt (int, char **, char *);
Xextern char *upcase (char *);
Xextern char *locase (char *);
Xextern void shrink (char *);
Xextern void free_remote (REMOTE **);
Xextern void distribute (FILE *, void (*)(char *, char *, BOOLEAN, BOOLEAN),
X			FILE *, char *, char *, char *, char *);
Xextern BOOLEAN extract_subscriber (FILE *, char *, BOOLEAN);
Xextern BOOLEAN sysmail (char *);
Xextern BOOLEAN strinstr (char *, char *);
Xextern BOOLEAN ignore_sender (FILE *, char *, FILE *, BOOLEAN);
Xextern BOOLEAN owner_listed (char *, char *, char *, FILE *);
Xextern int  lock_file (char *, int, int, BOOLEAN);
Xextern void unlock_file (int);
Xextern int  otoi (char *);
Xextern BOOLEAN mkdir1 (char *, char *, char *);
Xextern BOOLEAN make_indexes (char *, char *, char *, char *, char *);
Xextern int  re_strcmp (char *, char *, char *);
Xextern char *_strstr (char *, char *);
Xextern char *skip_to_word (char *, int);
Xextern char *mystrdup (char *);
Xextern int echo (char *, char *);
Xextern int echo_append (char *, char *);
Xextern int mv (char *, char *);
Xextern int cp (char *, char *);
Xextern int cat_append (char *, char *);
Xextern int touch (char *);
Xextern int ucb_strftime (char *, int, char *, struct tm *);
Xextern long int write_to_fd (int, char *, long int);
Xextern void escape_re (char *);
X
Xvoid   main (int, char **, char **);
Xvoid   create_header (FILE **, char *, char *, char *, char *, char *,
X		      BOOLEAN, BOOLEAN, BOOLEAN);
Xvoid   create_multi_recipient_header (FILE **, char *, char *, char *, char *,
X				      char *, int);
Xvoid   create_news_header (FILE **, char *, char *, char *, char *, char *);
Xvoid   create_gate_header (FILE **, char *, char *, char *, char *, char *,
X			   char *);
Xvoid   process_message (char *, char *, BOOLEAN, BOOLEAN);
Xvoid   distributions (BOOLEAN, char *, char *, BOOLEAN, char *, char *,
X		      char *, char *, char *, long int, BOOLEAN);
Xvoid   do_distribute (char *, FILE *, char *, BOOLEAN, char *, BOOLEAN, char *,
X		      char *, char *, char *, char *, FILE *,
X		      BOOLEAN, BOOLEAN, long int, BOOLEAN, BOOLEAN, BOOLEAN);
XBOOLEAN unprocessed_users (char *, char *, FILE *);
Xvoid   update_unprocessed (char *, BOOLEAN, BOOLEAN, int);
Xvoid   update_unprocessed_messages (char *);
XBOOLEAN copy_msg (FILE *, char *, BOOLEAN, char *, long int, BOOLEAN);
Xvoid   sendmail (char *, BOOLEAN, BOOLEAN, int, int, char *);
Xvoid   usage (void);
Xvoid   list_config (char *);
Xvoid   version (void);
Xint    gexit (int);
Xvoid   fill_text (FILE *, char *);
XBOOLEAN read_recipient (FILE *, char *, char *, char *, BOOLEAN);
Xlong int count_lines_in_file (FILE *);
Xvoid   digest_make (BOOLEAN);
Xvoid   digest_distribute (void);
Xvoid   do_archive (char *, char *, char *, BOOLEAN);
Xvoid   farch (char *, char *, char *, char *, char *, char *);
Xvoid   reject_archive (char *, char *);
XBOOLEAN get_volume (char *, int *, int *);
XBOOLEAN get_archive_name (char *, char *);
XBOOLEAN limit_exceeded (char *, char *, char *);
X
X/*
X  The control structure of the mail-distributor. Check if mail has arrived.
X  If so, copy it to MAIL_COPY and proceed to lower level.
X*/
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  char *options = "1fvrL:em:spPDMdi:";
X  int c;
X  char error [MAX_LINE];
X  int rlfd, i;
X  long int sig_mask;
X  FILE *f;
X  extern char *optarg, *getenv();
X  extern int optopt;
X#ifdef TCP_IP
X  struct hostent *lhost;
X#endif
X
X  prog = argv[0];
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case '1': execute_once = TRUE; break;
X      case 'f': errors_to_owner = TRUE; break;
X      case 'r': send_to_subscribers = FALSE; break;
X      case 'L': list_alias = upcase (optarg); break;
X      case 'e': tty_echo = TRUE; break;
X      case 's': do_not_check_subscriptions = TRUE; break;
X      case 'p': article_replies_to_author = TRUE; break;
X      case 'P': message_replies_to_author = TRUE; break;
X      case 'v': version ();
X      case 'D': debug = TRUE; break;
X      case 'd': force_digest = TRUE; break;
X      case 'i': one_digest = optarg; break;
X      case 'M': is_moderated = TRUE; break;
X      case 'm':
X	multi_recip = TRUE;
X	if ((maxrecipients = atoi (optarg)) < 1)
X	  fprintf (stderr, "-m %d -- yeah, right!\n", maxrecipients),
X	  exit (3);
X	break;
X      case ':': 
X	fprintf (stderr, "list: Option '%c' requires an argument.\n", optopt);
X	exit (3);
X      case '?':
X      default:
X	usage ();
X    }
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (LIST_LOCK_FILE, O_RDWR, 0, FALSE)) < 0)
X    fprintf (stderr, "list: Unable to lock %s. Aborting.\n", LIST_LOCK_FILE),
X    exit (1);
X#endif
X  init_signals();
X  catch_signals();
X  list_config (list_alias);
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  if ((report = fopen (report_listf, "a")) == NULL)
X    if ((report = fopen (REPORT_LIST, "a")) == NULL)
X      fprintf (stderr, "list: Could not open %s and %s\n", report_listf,
X	       REPORT_LIST),
X      exit (1);
X    else
X      chmod (REPORT_LIST, 384); /* 600 */
X  else
X    chmod (report_listf, 384); /* 600 */
X#endif
X  if (list_alias == NULL)
X    report_progress (report, "\nlist: No list to process", TRUE),
X    exit (3);
X  nlists = sys_config (report, &sys);
X  if ((listid = get_list_id (list_alias, &sys, nlists)) < 0)
X    report_progress (report, tsprintf ("\nlist: Unknown list %s", list_alias),
X		     TRUE),
X    exit (3);
X  if (!execute_once)
X    printf ("%s", COPYRIGHT);
X  if (sys.options & USE_ENV_VAR) {
X    if ((sys.mail.method = (char *) malloc (256 * sizeof (char))) == NULL)
X      report_progress (report, "\nmain(): malloc() failed", TRUE),
X      exit (11);
X    sprintf (sys.mail.method, "env - %s=%s %s ", sys.mail.env_var,
X             sys.lists[listid].address, sys.mail.mail_prog);
X  }
X
X  if ((msg_no = fopen (msg_nof, "r")) != NULL)
X    fscanf (msg_no, "%d %d\n", &public_msg, &returned_msg),
X    fclose (msg_no);
X  if ((digest_no = fopen (digest_nof, "r")) != NULL)
X    fscanf (digest_no, "%d\n", &digest_msg),
X    fclose (digest_no);
X#if defined (TIOCNOTTY) && defined (SIGTTOU) && defined (SIGTTIN)
X  if (!tty_echo)
X    if ((i = open ("/dev/tty", 2)) >=0)
X      ioctl (i, TIOCNOTTY, 0),
X      close (i);
X#endif
X
X  if (multi_recip)
X    if ((multi_recipients = (char **) malloc (maxrecipients * sizeof (char *)))
X	== NULL)
X      report_progress (report, "\nmain(): malloc() failed", TRUE),
X      exit (11);
X
X  if ((f = fopen (PID_LIST, "w")) != NULL)
X    fprintf (f, "%d", getpid()),
X    fclose (f);
X  signal (SIGINT, (void (*)()) gexit);
X  signal (SIGALRM, SIG_IGN);
X#ifdef TCP_IP
X  gethostname (hostname, sizeof (hostname));
X  lhost = gethostbyname (hostname);
X  memcpy ((char *) &localaddr, (char *) lhost->h_addr, lhost->h_length);
X#else
X  strcpy (localaddr, LOCAL_ADDR);
X  strcpy (hostname, HOSTNAME);
X#endif
X
X  sprintf (error, "recipients of digest %d", digest_msg);
X  if (unprocessed_users (unprocessed_digestf, error, report)) {
X    /* Was interrupted while distributing a digest */
X    /* USER CONTIBUTED CODE: Warren Burstein */
X    OPEN_FILE (subscribers, subscribersf, "r", "main");
X    OPEN_FILE (news, newsf, "r", "main");
X    OPEN_FILE (peers, peersf, "r", "main");
X    OPEN_FILE (restricted, restrictedf, "r", "main");
X
X    digest_distribute();
X
X    fclose (subscribers);
X    fclose (news);
X    fclose (peers);
X    fclose (restricted);
X  }
X
X  if ((force_digest || one_digest) &&
X      (!stat (digest_msgf, &stat_buf) && stat_buf.st_size > 0)) {
X    OPEN_FILE (subscribers, subscribersf, "r", "main");
X    OPEN_FILE (news, newsf, "r", "main");
X    OPEN_FILE (peers, peersf, "r", "main");
X    OPEN_FILE (restricted, restrictedf, "r", "main");
X
X    if (force_digest) {
X      digest_make (TRUE);
X      digest_distribute ();
X    }
X    else { /* Make a partial digest, send to one_digest */
X      char subject[MAX_LINE];
X      FILE *forwardmail = NULL; /* Completed message to be forwarded */
X
X      digest_make (FALSE);
X
X      sprintf (subject, "%s partial digest %d", sys.lists[listid].alias,
X	       digest_msg);
X
X      create_header (&forwardmail, mailforwardf, sys.lists[listid].address,
X		     sys.lists[listid].address, one_digest, subject,
X		     FALSE, FALSE, FALSE);
X      if (copy_msg (forwardmail, msgf, FALSE, sys.lists[listid].address, 0,
X		    FALSE))
X        sendmail (one_digest, FALSE, FALSE, 0, 0, mailforwardf);
X    }
X    fclose (subscribers);
X    fclose (news);
X    fclose (peers);
X    fclose (restricted);
X  }
X
X  do {
X    if (!stat (list_mail_f, &stat_buf) && stat_buf.st_size > 0) {
X#ifdef bsd
X      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X      sighold (SIGINT);
X      sighold (SIGTERM);
X#endif
X#ifndef NO_LOCKS
X      if ((rlfd = lock_file (list_mail_f, O_RDWR, 0, FALSE)) < 0)
X	break;  /* Cannot lock file, so exit, regardless of execute_once */
X#endif
X      if (!sys.lists[listid].max_messages) {
X	if (cp (list_mail_f, mail_copyf))
X	  exit (16);
X	unlink (list_mail_f);
X	touch (list_mail_f); /* rewrite file */
X	chmod (list_mail_f, /*416*/ 0666 & (0666 ^ otoi (mask)));
X      }
X      else if (limit_exceeded (limitsf, list_mail_f, mail_copyf)) {
X#ifndef NO_LOCKS
X        unlock_file (rlfd);
X#endif
X	break;
X      }
X      if (cat_append (mail_copyf, mboxf))
X	exit (16);
X      chmod (mboxf, 384);
X      if (cp (mail_copyf, unprocessed_messages))
X	exit (16);
X      echo_append ("", mboxf);
X      touch (list_moderated_f);
X      chmod (list_moderated_f, /*416*/ 0666 & (0666 ^ otoi (mask)));
X
X#ifndef NO_LOCKS
X      unlock_file (rlfd);
X#endif
X#ifdef bsd
X      sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X      sigrelse (SIGINT);
X      sigrelse (SIGTERM);
X#endif
X
X      OPEN_FILE (mail, mail_copyf, "r", "main");
X      OPEN_FILE (subscribers, subscribersf, "r", "main");
X      OPEN_FILE (news, newsf, "r", "main");
X      OPEN_FILE (peers, peersf, "r", "main");
X      OPEN_FILE (restricted, restrictedf, "r", "main");
X      OPEN_FILE (ignored, ignoredf, "r", "main");
X      OPEN_FILE (headers, headersf, "a", "main");
X      report_progress (report, NEW_ARRIVAL, FALSE);
X      distribute (mail,
X		  (void (*)(char *, char *, BOOLEAN, BOOLEAN)) process_message,
X		  report, subscribersf, newsf, peersf, aliasesf);
X      fclose (mail);  /* Done */
X      fclose (subscribers);
X      fclose (news);
X      fclose (peers);
X      fclose (restricted);
X      fclose (ignored);
X      fclose (headers);
X      shrink (message_idsf);
X      unlink (unprocessed_messages);
X      unlink (mail_copyf);  /* Done delivering */
X    }
X    else if (!execute_once) /* No mail to deliver */
X      if (sys.frequency > 0)
X        sleep (sys.frequency);
X  } while (!execute_once);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X#ifdef SYSLOG
X  closelog ();
X#else
X  fclose (report);
X#endif
X  free_remote (&rlists);
X  if (multi_recip)
X    free ((char **) multi_recipients);
X  gexit (0);
X}
X
X/* 
X  Create a mail header. If this is a reply to a rejected posting, 'copy_owner'
X  should be TRUE. If reply_to_sender is TRUE, message will reply to sender,
X  otherwise to list.
X*/
X
Xvoid create_header (FILE **f, char *filename, char *sender, char *originator,
X		    char *recipient, char *subject, BOOLEAN copy_owner,
X		    BOOLEAN reply_to_sender, BOOLEAN error_condition)
X{
X  char error [MAX_LINE];
X  char origin [MAX_LINE];
X  char reply_to [MAX_LINE];
X#ifdef LIST_ALIAS_IN_SUBJECT
X  char *s, *sb;
X#endif
X#ifdef NEED_DATE
X  char date [80];
X  long int time_is;
X  struct tm *t;
X#endif
X  MATCHED_PREC_HEADER *m = matched_prec_header;
X
X  strcpy (origin, originator);
X  locase (origin);
X  strcpy (reply_to, sender);
X  extract_address (reply_to);
X  OPEN_FILE (*f, filename, "w", "create_header");
X  locase (recipient);
X  if (subject [0] != EOS && subject [strlen (subject) - 1] == '\n')
X    subject [strlen (subject) - 1] = EOS;
X  if (sys.options & USE_TELNET) {
X    fprintf (*f, "HELO %s\nMAIL From: <%s>\nRCPT To: <%s>\n",
X#ifdef ZMAILER
X/*
X# ifdef TCP_IP
X	     (char *) inet_ntoa (localaddr),
X# else
X	     localaddr,
X# endif
X	Use either the local address or the host name.
X*/
X	     hostname,
X#else
X	     "",
X#endif
X	     sys.lists[listid].address, recipient);
X    if (copy_owner)
X      fprintf (*f, "RCPT To: <%s>\n", sys.lists[listid].owner);
X    fprintf (*f, "DATA\n");
X  }
X  if (message_id[0] != EOS)
X    fprintf (*f, "Message-Id: %s\n", message_id);
X#ifdef NEED_DATE
X  time (&time_is);
X  t = localtime (&time_is);
X  ucb_strftime (date, 1024, "%a, %e %b %Y %T %Z", t);
X  fprintf (*f, "Date: %s\n", date);
X#endif
X#ifndef NO_ERRORS_TO
X  fprintf (*f, "Errors-To: %s\n", sys.lists[listid].owner);
X#endif
X  while (m) {
X    if (!strncmp ("Reply-To:", m->line, strlen ("Reply-To:")))
X      break;
X    m = m->next;
X  }
X  if (!m)
X    fprintf (*f, "Reply-To: %s\n",
X	     ((reply_to_sender | message_replies_to_author) ? 
X	      reply_to : sys.lists[listid].address));
X  fprintf (*f, "Originator: %s\nSender: %s\nPrecedence: %s\nFrom: %s\nTo: %s\n",
X	   origin, sys.lists[listid].address, sys.mail.precedence, sender,
X	   recipient);
X  if (copy_owner)
X    fprintf (*f, "Cc: %s\n", sys.lists[listid].owner);
X#ifdef LIST_ALIAS_IN_SUBJECT
X  if (!error_condition) {
X    /* Remove previous tag and amassing Re: */
X    if ((s = _strstr (subject, tsprintf ("[%s:", sys.lists[listid].alias)))) {
X      sprintf (s, "%s", ((sb = strchr (s, ']')) ? sb + 2 : ""));
X      if (re_strcmp ("^[ \t]*[Rr][Ee]:[ \t]+[Rr][Ee]:", subject, NULL) > 0)
X	sprintf (subject, "%s", subject + 4);
X    }
X    fprintf (*f, "Subject: [%s:%d] %s\n", sys.lists[listid].alias, public_msg,
X	     subject);
X  }
X  else
X    fprintf (*f, "Subject: %s%s\n", ERROR_CONDITION, subject);
X#else
X  fprintf (*f, "Subject: %s%s\n", (error_condition ? ERROR_CONDITION : ""),
X	   subject);
X#endif
X  fprintf (*f, "X-Listserver-Version: %s\n", VERSION);
X  if (sys.lists[listid].comment[0] != EOS)
X    fprintf (*f, "X-Comment: %s\n", sys.lists[listid].comment);
X  m = matched_prec_header;
X  while (m)
X    fprintf (*f, "%s", m->line),
X    m = m->next;
X  fprintf (*f, "\n");
X}
X
X/* 
X  Create a mail header with multiple recipients.
X*/
X
Xvoid create_multi_recipient_header (FILE **f, char *filename, char *sender,
X				    char *originator, char *recipient,
X				    char *subject, int nrecipients)
X{
X  char origin [MAX_LINE];
X  char reply_to [MAX_LINE];
X  int  i;
X  MATCHED_PREC_HEADER *m = matched_prec_header;
X#ifdef LIST_ALIAS_IN_SUBJECT
X  char *s, *sb;
X#endif
X#ifdef NEED_DATE
X  char date [80];
X  long int time_is;
X  struct tm *t;
X#endif
X
X  strcpy (origin, originator);
X  locase (origin);
X  strcpy (reply_to, sender);
X  extract_address (reply_to);
X  if (subject [0] != EOS && subject [strlen (subject) - 1] == '\n')
X    subject [strlen (subject) - 1] = EOS;
X  OPEN_FILE (*f, filename, "w", "create_multi_recipient_header");
X  if (sys.options & USE_TELNET) {
X    fprintf (*f, "HELO %s\nMAIL From: <%s>\n",
X#ifdef ZMAILER
X/*
X# ifdef TCP_IP
X	     (char *) inet_ntoa (localaddr),
X# else
X	     localaddr,
X# endif
X	Use either the local address or the host name.
X*/
X	     hostname,
X#else
X	     "",
X#endif
X	     sys.lists[listid].address);
X    for (i = 0; i < nrecipients; i++)
X      fprintf (*f, "RCPT To: <%s>\n", multi_recipients[i]),
X      free ((char *) multi_recipients[i]);
X    fprintf (*f, "DATA\n");
X  }
X  if (message_id[0] != EOS)
X    fprintf (*f, "Message-Id: %s\n", message_id);
X#ifdef NEED_DATE
X  time (&time_is);
X  t = localtime (&time_is);
X  ucb_strftime (date, 1024, "%a, %e %b %Y %T %Z", t);
X  fprintf (*f, "Date: %s\n", date);
X#endif
X#ifndef NO_ERRORS_TO
X  fprintf (*f, "Errors-To: %s\n", sys.lists[listid].owner);
X#endif
X  while (m) {
X    if (!strncmp ("Reply-To:", m->line, strlen ("Reply-To:")))
X      break;
X    m = m->next;
X  }
X  if (!m)
X    fprintf (*f, "Reply-To: %s\n",
X	     (message_replies_to_author ? reply_to : sys.lists[listid].address));
X  fprintf (*f, "Originator: %s\nSender: %s\nPrecedence: %s\nFrom: %s\nTo: %s\n",
X	   origin, sys.lists[listid].address, sys.mail.precedence, sender,
X	   recipient);
X#ifdef LIST_ALIAS_IN_SUBJECT
X  /* Remove previous tag and amassing Re: */
X  if ((s = _strstr (subject, tsprintf ("[%s:", sys.lists[listid].alias)))) {
X    sprintf (s, "%s", ((sb = strchr (s, ']')) ? sb + 2 : ""));
X    if (re_strcmp ("^[ \t]*[Rr][Ee]:[ \t]+[Rr][Ee]:", subject, NULL) > 0)
X      sprintf (subject, "%s", subject + 4);
X  }
X  fprintf (*f, "Subject: [%s:%d] %s\n", sys.lists[listid].alias, public_msg,
X	   subject);
X#else
X  fprintf (*f, "Subject: %s\n", subject);
X#endif
X  fprintf (*f, "X-Listserver-Version: %s\n", VERSION);
X  if (sys.lists[listid].comment[0] != EOS)
X    fprintf (*f, "X-Comment: %s\n", sys.lists[listid].comment);
X  m = matched_prec_header;
X  while (m)
X    fprintf (*f, "%s", m->line),
X    m = m->next;
X  fprintf (*f, "\n");
X}
X
X/*
X  Create a news header for a message to be posted.
X*/
X
Xvoid create_news_header (FILE **f, char *filename, char *sender,
X			 char *originator, char *group, char *subject)
X{
X  char origin [MAX_LINE];
X  char reply_to [MAX_LINE];
X  MATCHED_PREC_HEADER *m = matched_prec_header;
X#ifdef NEED_DATE
X  char date [80];
X  long int time_is;
X  struct tm *t;
X#endif
X
X  strcpy (origin, originator);
X  locase (origin);
X  strcpy (reply_to, sender);
X  extract_address (reply_to);
X  if (subject [0] != EOS && subject [strlen (subject) - 1] == '\n')
X    subject [strlen (subject) - 1] = EOS;
X  OPEN_FILE (*f, filename, "w", "create_news_header");
X  locase (group);
X  fprintf (*f, "Path: %s\nNewsgroups: %s\nDistribution: world\n\
XOrganization: %s\nOriginator: %s\n",
X	   (article_replies_to_author ? reply_to : sys.lists[listid].address),
X	   group, sys.organization, origin);
X  if (message_id[0] != EOS)
X    fprintf (*f, "Message-Id: %s\n", message_id);
X#ifdef NEED_DATE
X  time (&time_is);
X  t = localtime (&time_is);
X  ucb_strftime (date, 1024, "%a, %e %b %Y %T %Z", t);
X  fprintf (*f, "Date: %s\n", date);
X#endif
X  while (m) {
X    if (!strncmp ("Reply-To:", m->line, strlen ("Reply-To:")))
X      break;
X    m = m->next;
X  }
X  if (!m)
X    fprintf (*f, "Reply-To: %s\n",
X	     (article_replies_to_author ? reply_to : sys.lists[listid].address));
X  fprintf (*f, "Sender: %s\nFrom: %s\nSubject: %s\n", 
X	   sys.lists[listid].address, sender, subject);
X  m = matched_prec_header;
X  while (m)
X    fprintf (*f, "%s", m->line),
X    m = m->next;
X  fprintf (*f, "\n");
X}
X
X/*
X  Create a gateway header.
X*/
X
Xvoid create_gate_header (FILE **f, char *filename, char *sender,
X			 char *originator, char *recipient, 
X			 char *group, char *subject)
X{
X  char origin [MAX_LINE];
X  char reply_to [MAX_LINE];
X  MATCHED_PREC_HEADER *m = matched_prec_header;
X#ifdef NEED_DATE
X  char date [80];
X  long int time_is;
X  struct tm *t;
X#endif
X
X  strcpy (origin, originator);
X  locase (origin);
X  strcpy (reply_to, sender);
X  extract_address (reply_to);
X  if (subject [0] != EOS && subject [strlen (subject) - 1] == '\n')
X    subject [strlen (subject) - 1] = EOS;
X  OPEN_FILE (*f, filename, "w", "create_gate_header");
X  locase (group);
X  if (sys.options & USE_TELNET)
X    fprintf (*f, "HELO %s\nMAIL From: <%s>\nRCPT To: <%s>\nDATA\n",
X#ifdef ZMAILER
X/*
X# ifdef TCP_IP
X	     (char *) inet_ntoa (localaddr),
X# else
X	     localaddr,
X# endif
X	Use either the local address or the host name.
X*/
X	     hostname,
X#else
X	     "",
X#endif
X	     sys.lists[listid].address, recipient);
X#ifndef NO_ERRORS_TO
X  fprintf (*f, "Errors-To: %s\n", sys.lists[listid].owner);
X#endif
X  fprintf (*f, "Newsgroups: %s\nDistribution: world\nOrganization: %s\n\
XOriginator: %s\n", group, sys.organization, origin);
X  if (message_id[0] != EOS)
X    fprintf (*f, "Message-Id: %s\n", message_id);
X#ifdef NEED_DATE
X  time (&time_is);
X  t = localtime (&time_is);
X  ucb_strftime (date, 1024, "%a, %e %b %Y %T %Z", t);
X  fprintf (*f, "Date: %s\n", date);
X#endif
X  while (m) {
X    if (!strncmp ("Reply-To:", m->line, strlen ("Reply-To:")))
X      break;
X    m = m->next;
X  }
X  if (!m)
X    fprintf (*f, "Reply-To: %s\n",
X	     (article_replies_to_author ? reply_to : sys.lists[listid].address));
X  fprintf (*f, "Sender: %s\nFrom: %s\nTo: %s\nSubject: %s\n",
X	   sys.lists[listid].address, sender, recipient, subject);
X  m = matched_prec_header;
X  while (m)
X    fprintf (*f, "%s", m->line),
X    m = m->next;
X  fprintf (*f, "\n");
X}
X
X/*
X  The heart of the distribution. Since we are now at the beginning of the
X  message (as guaranteed by distribute ()), gather the rest of the message
X  -- that is until the beginning of the next message, or EOF. Note that
X  only the "From: ", "Originator: " and "Subject: " lines from the header
X  are saved.
X  The beginning of the next and every message is "From ". The actual
X  text of each message is separated from its header by a blank line (universal
X  format). The message is processed only if the sender is not listed in
X  IGNORED.
X  The entire message is stored in MSG. To distribute the message, prepare
X  a new header; the completed message is stored in MAILFORWARD. Actually,
X  for each subscriber we prepare a different header (different "To:" field) 
X  and append MSG to MAILFORWARD. Distribute that mail to the members,
X  or, either return it to the sender if he/she is not subscribed, or
X  forward it to MANAGER if -f specified. Messages from MAILER_DAEMON are
X  always forwarded to MANAGER. A message from a mailer daemon is identified
X  by the presence of MAILER_DAEMON anywhere in the sender's email address,
X  to cover local and remote mailer daemon addresses. Please look at the
X  documentation for defs.h for the syntax of the MAILER_DAEMON string.
X  The parameter "sender" is used for report purposes and when we are sending
X  the sender's message back to him/her.
X  Messages from regular subscribers are sent to peer lists and news groups.
X  Messages from news groups are only distributed to local subscribers and
X  peer lists. Finally, messages from peer lists are distributed locally
X  and may be posted to news groups.
X  In the case of restricted mail, check if the sender is listed in 
X  RESTRICTED; if so, forward the message to the people listed in the
X  filename found after the restricted-sender's address in RESTRICTED.
X  Each public message (legitimate message) is automatically archived.
X*/
X
Xchar *msgs[] = { /* USER CONTRIBUTED CODE: Warren Burstein */
X    "If you forward it back to the list, it will be distributed without the",
X    "paragraphs above the dashed line.  You may edit the Subject: line and",
X    "the text of the message before forwarding it back.",
X    "",
X    "If you edit the messages you receive into a digest, you will need to",
X    "remove these paragraphs and the dashed line before mailing the result", 
X    "to the list. Finally, if you need more information from the author of",
X    "this message, you should be able to do so by simply replying to this",
X    "note.",
X    "", NULL
X};
X
Xvoid process_message (char *sender, char *linecopy, BOOLEAN address_ok,
X		      BOOLEAN sender_subscribed)
X{
X  char line [MAX_LINE];            /* ... from the current message */
X  char sent_date [MAX_LINE];
X  char senders_subject [MAX_LINE]; /* the sender's subject */
X  char subject_copy [MAX_LINE];    /* a copy of the subject */
X  char original_sender [MAX_LINE]; /* as it appears in his message */
X  char originator [MAX_LINE];	   /* Holds the Originator: address */
X  char reply_to [MAX_LINE];	   /* Holds the Reply-To: address */
X  char id_copy [MAX_LINE];	   /* Holds the Message-Id: */
X  char match [MAX_LINE];
X  char match2 [MAX_LINE];
X  char error [MAX_LINE];
X  char received [MAX_LINE];
X  char sender_copy [MAX_LINE];
X  char *at = NULL;
X  FILE *forwardmail = NULL;        /* completed message to be forwarded */
X  FILE *msg = NULL;
X  FILE *header = NULL;
X  FILE *in, *out, *f;
X  BOOLEAN already_sent = FALSE;
X  BOOLEAN message_forwarded = FALSE;
X  BOOLEAN mailer_daemon;
X  BOOLEAN susp_subject;
X  BOOLEAN fake_mail = TRUE;
X  BOOLEAN control_article = FALSE;
X  long int lines_to_skip = 0;	  /* skip lines of incoming message */
X  long int sig_mask;
X  MATCHED_PREC_HEADER *out_header = matched_prec_header;
X  PRECIOUS_HEADER *pheader;
X  int request_found, i;
X
X  /* gets set if anyone wants digests */
X  append_to_digest = sys.lists[listid].options & ARCHIVE_DIGEST;
X
X  line[0] = sent_date[0] =
X    senders_subject[0] = 
X      subject_copy[0] = 
X	originator[0] =
X          archive_name[0] =
X	    reply_to[0] =
X	      message_id[0] =
X		id_copy[0]=
X		  received[0] =
X		    RESET (original_sender);
X
X  strcpy (sender_copy, sender);
X  upcase (sender_copy);
X  while (matched_prec_header)	/* Free previous precious header lines */
X    out_header = matched_prec_header->next,
X    free ((char *) matched_prec_header->line),
X    free ((MATCHED_PREC_HEADER *) matched_prec_header),
X    matched_prec_header = out_header;
X
X  /* Remove message header, but store the "From: " and "Subject: " fields to
X     be used later when sending the message to all subscribers. Also, preserve
X     user-defined header lines. */
X
X  OPEN_FILE (header, headerf, "w", "process_message");
X  fprintf (header, "%s", linecopy);
X  while (!feof (mail) && line[0] != '\n') {
X    strcpy (match, "\\1");
X    if (re_strcmp (FROM, line, match) > 0) {
X      strcpy (original_sender, line + strlen (match)); /* Save From: */
X      original_sender [strlen (original_sender) - 1] = EOS; /* \n -> \0 */
X      if (strchr (original_sender, '|') || original_sender[0] == ':' ||
X	  strchr (original_sender, '`'))
X	original_sender[0] = EOS;	/* protect against Trojans */
X    }
X
X    else if (re_strcmp (DATE, line, match) > 0)
X      strcpy (sent_date, line + strlen (match)), /* Save Date: */
X      sent_date [strlen (sent_date) - 1] = EOS; /* \n -> \0 */
X
X    else if (re_strcmp (SUBJECT, line, match) > 0)
X      strcpy (senders_subject, line + strlen (match)), /* Save Subject: */
X      senders_subject [strlen (senders_subject) - 1] = EOS, /* \n -> \0 */
X      strcpy (subject_copy, senders_subject);
X
X    else if (re_strcmp (ORIGIN, line, match) > 0)
X      strcpy (originator, line + strlen (match)),  /* Remove "Originator: " */
X      extract_origin (originator),
X      upcase (originator);
X
X    else if (re_strcmp (ARCHIVE_NAME, line, match) > 0)
X      /* Remove "Archive-Name: " */
X      strcpy (archive_name, line + strlen (match)),
X      extract_origin (archive_name);
X
X    else if (re_strcmp (REPLY_TO, line, match) > 0)
X      strcpy (reply_to, line + strlen (match)), /* Remove "Reply-To: " */
X      extract_origin (reply_to),
X      upcase (reply_to);
X
X    else if (re_strcmp (RECEIVED, line, match) > 0) { /* Check for fake mail */
X      strcpy (received, line + strlen (match));
X      received [strlen (received) - 1] = EOS;
X      sscanf (received, "%s %s", error, received); /* Get host after "from" */
X      upcase (received);  /* Relay host */
X      if (fake_mail && (at = strchr (sender_copy, '@')))
X	if (re_strcmp (at + 1, received, NULL) > 0)
X	  fake_mail = FALSE;
X    }
X
X    else if (re_strcmp (CONTROL, line, NULL) > 0)
X      control_article = TRUE;
X
X    else if (re_strcmp (MESSAGE_ID1, line, NULL) > 0 ||
X             re_strcmp (MESSAGE_ID2, line, NULL) > 0 ||
X	     re_strcmp (MESSAGE_ID3, line, NULL) > 0)
X      strcpy (message_id, line + strlen ("Message-") + 4),
X      message_id [strlen (message_id) - 1] = EOS; /* \n -> \0 */
X
X    else {	/* Check if precious header line */
X      pheader = sys.lists[listid].header;
X      while (pheader) {
X        if (!strncmp (line, pheader->line, strlen (pheader->line))) {
X	  if (! (out_header =
X		 (MATCHED_PREC_HEADER *) malloc (sizeof (MATCHED_PREC_HEADER))))
X	    report_progress (report, "\nprocess_message(): malloc() failed",
X			     TRUE),
X	    exit (11);
X	  if (! (out_header->line =
X		 (char *) malloc ((strlen (line) + 1) * sizeof (char ))))
X	    report_progress (report, "\nprocess_message(): malloc() failed",
X			     TRUE),
X	    exit (11);
X	  strcpy (out_header->line, line);
X	  out_header->next = matched_prec_header;
X	  matched_prec_header = out_header;
X	}
X	pheader = pheader->next;
X      }
X    }
X
X    RESET (line);
X    fgets (line, MAX_LINE - 2, mail);
X    fprintf (header, "%s", line);
X  }
X  fclose (header);
X
X  /* Gather message into MSG; check if we have reached the next message, 
X     or EOF; also check for possible requests sent to the list */
X  OPEN_FILE (msg, msgf, "w", "process_message");
X  RESET (line);
X  request_found = -1;
X  while (!feof (mail) && 
X	 (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {
X    if (!strcmp (line, ".\n"))
X      PREPEND (".", line);
X    fprintf (msg, "%s", line);
X    upcase (line);
X    if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X      line [strlen (line) - 1] = EOS;
X    if (request_found < 0 && line[0] != EOS) { /* First non-empty line */
X      strcpy (match, "\\1");
X      if (re_strcmp (REQUESTS, line, match) > 0) { /* Possible request */
X#ifdef LIST_CHECKING_FOR_REQUESTS
X	strcpy (match2, "\\2");
X	re_strcmp (REQUESTS, line, match2); /* Get list specified (if any) */
X	if (match2[0] == EOS)
X	  request_found = TRUE; /* Request does not take 'list' arg */
X	else if (get_list_id (match2, &sys, nlists) >= 0)
X#endif
X	  request_found = TRUE;
X      }
X      if (request_found < 0)
X	request_found = FALSE;
X    }
X    fgets (line, MAX_LINE - 2, mail);
X  }
X  fclose (msg);
X
X  strcpy (linecopy, line); /*** VERY IMPORTANT: we are now at the next msg ***/
X
X  if (!address_ok) {		/* Abort message */
X    NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS (sender, "process_message", TRUE);
X    return;
X  }
X
X  if (control_article) {
X    report_progress (report, "Control message ignored", TRUE);
X    return;
X  }
X  /* Check the IGNORED file */
X  if (ignore_sender (ignored, sender, report, FALSE))
X    return;
X  if (originator[0] != EOS) /* Check for mail loop using Originator: */
X    if (ignore_sender (ignored, originator, report, FALSE))
X      return;
X  if (reply_to[0] != EOS) /* Check for mail loop using Reply-To: */
X    if (ignore_sender (ignored, reply_to, report, FALSE))
X      return;
X  if (message_id[0] != EOS) { /* Check for mail loop using Message-Id: */
X    strcpy (id_copy, message_id);
X    upcase (id_copy);
X    if (message_ids = fopen (message_idsf, "r")) {
X      if (ignore_sender (message_ids, id_copy, report, TRUE)) {
X        fclose (message_ids);
X        return;
X      }
X      fclose (message_ids);
X    }
X  }
X  if (sender_subscribed == NEWS)
X    if (ignore_sender (peers, originator, report, TRUE))
X      return;
X
X  if (originator[0] == EOS) /* Fresh message originating from this list */
X    sprintf (originator, "%s", sys.lists[listid].address);
X  upcase (originator);
X
X  if (do_not_check_subscriptions)
X    sender_subscribed = SUBSCRIBED;
X  mailer_daemon = strinstr (MAILER_DAEMON, sender) ||
X    (original_sender[0] != EOS && strinstr (MAILER_DAEMON, original_sender)) ||
X    *sender == EOS;
X  susp_subject = (senders_subject[0] != EOS &&
X		  strinstr (SUSP_SUBJECT, senders_subject));
X  if (sender_subscribed == NOTSUBSCRIBED || mailer_daemon || susp_subject ||
X      request_found > 0) {
X    /* Send error message to sender */
X    report_progress (report,
X		     tsprintf ("Invalid message #%04d (%s). Forwarding error \
Xmessage to %s.\n%s",
X			       ++returned_msg,
X			       (mailer_daemon ? "suspicious address" :
X				(susp_subject ? "suspicious subject" :
X				 (request_found > 0 ? "request sensed" : 
X				  (sender_subscribed == NOTSUBSCRIBED ?
X				   "sender not subscribed" :
X				   "???")))),
X			       ((mailer_daemon || susp_subject || errors_to_owner) ?
X				sys.lists[listid].owner : sender),
X			       (*sender != EOS ? sender : "\"\"")), TRUE);
X    OPEN_FILE (msg_no, msg_nof, "w", "process_message");
X    fprintf (msg_no, "%d %d\n", public_msg, returned_msg);
X    fclose (msg_no);
X    OPEN_FILE (msg, msgf, "r", "process_message");
X    /* Prepare header */
X    create_header (&forwardmail, mailforwardf, sys.lists[listid].address,
X      originator,
X      ((mailer_daemon || susp_subject || errors_to_owner) ?
X       sys.lists[listid].owner : sender),
X      senders_subject, COPY_OWNER (ccerrors), FALSE, TRUE);
X    if (errors_to_owner || mailer_daemon || susp_subject)
X      fprintf (forwardmail, "\nRejected message: sent to %s by %s \
Xfollows.\nReason for rejection: %s.\n",
X	       sys.lists[listid].address, (*sender != EOS ? sender : "\"\""),
X	       (mailer_daemon ? "suspicious address" :
X		(susp_subject ? "suspicious subject" :
X		 (request_found > 0 ? "request sensed" : 
X		  (sender_subscribed == NOTSUBSCRIBED ? "sender not subscribed" :
X		   "???")))));
X    else if (request_found > 0)
X      if (sender_subscribed != PEER && sender_subscribed != NEWS)
X	/* First line of msg contained a request; send it back w/ apologies */
X	fprintf (forwardmail, "\nWe are sorry, but this system sensed the \
Xfollowing request which may have been\ninadvertedly sent to this list:\n\n%s\n\
X\nIf your posting was intentional, please accept our apologies and resend your\
X\nmail message, making sure you do not include anything that may look like a\
X\nrequest in the first line of the body of the actual message. If this was \
X\nindeed a request please resend it to %s. Your entire message\nis copied \
Xbelow.\n\n", match, sys.server.address);
X      else
X	report_progress (report, "Message from peer/news ignored: request \
Xsensed\n", FALSE);
X    else {
X      fprintf (forwardmail, "%s: You are not subscribed to %s.\n\
XYour message is returned to you unprocessed. If you want to subscribe,\n\
Xsend mail to %s with the following request:\n\n\t\tsubscribe %s Your Name\n\n",               sender, sys.lists[listid].address, sys.server.address,
X               sys.lists[listid].alias);
X      if (alternate_addresses) {
X	fprintf (forwardmail, "In addition, the system found the following \
Xaddress(es) that resemble yours.\nIf one of these is you, please resend your \
Xmessage from that one, or use the\n'set <list> address' request to change the \
Xaddress you are subscribed with:\n\n");
X	for (i = 0; alternate_addresses[i]; ++i)
X	  fprintf (forwardmail, "%s\n", alternate_addresses[i]),
X	  free ((char *) alternate_addresses[i]);
X	free ((char **) alternate_addresses);
X	alternate_addresses = NULL;
X	fprintf (forwardmail, "\n");
X      }
X    }
X
X#ifdef ERROR_MAIL_ANALYSIS
X    if (mailer_daemon) { /* See if we should remove the user and/or log error */
X      char line [1024], matches [MAX_LINE], matches2 [MAX_LINE],
X        subj [MAX_LINE];
X      BOOLEAN fatal_subject;
X
X      RESET (line);
X      rewind (msg);
X      strcpy (subj, subject_copy);
X      upcase (subj);
X      while (!feof (msg) &&
X	     !_strstr (line, "-- Unsent message follows --")) {
X	fgets (line, 1023, msg);
X	upcase (line);
X	strcpy (matches, "\\1.*\\2");
X	strcpy (matches2, "\\1");
X	if (re_strcmp (errors, line, matches) > 0 ||
X	    ((fatal_subject = re_strcmp (fatal_subjects, subj, matches2)) > 0 &&
X	     ((atoi (matches2) >= (GRACE_PERIOD / 86400)) ||
X	      matches2[0] == EOS))) {
X	  /* Remove user if present */
X	  char address [MAX_LINE], address_copy [MAX_LINE],_line [1024],
X	    _address [MAX_LINE];
X
X# if (ERROR_MAIL_ANALYSIS == 9)
X	  if (fatal_subject > 0)
X	    strcpy (matches, ".*\\1"), /* See if warning and get host name */
X	    re_strcmp (warnings, line, matches);
X	  if (matches [0] != EOS && matches [strlen (matches) - 1] == '\n')
X	    matches [strlen (matches) - 1] = EOS;
X# endif
X	  rewind (subscribers);
X	  while (!feof (subscribers)) {
X	    address [0] = RESET (_line);
X	    fgets (_line, MAX_LINE - 2, subscribers);
X	    sscanf (_line, "%s", address);
X	    upcase (_line);
X	    upcase (address);
X	    strcpy (address_copy, address);
X	    escape_re (address);
X	    if (
X# if (ERROR_MAIL_ANALYSIS == 9)
X		strcmp (matches, ".*") && strcmp (matches, ".*\\1") &&
X# endif
X		strcmp (matches, "\\1.*\\2"))
X	      sprintf (_address, "^[ \t]*%s", matches +
X		       (!strncmp (matches, ".*", 2) ? 2 : 0));
X	    else
X	      strcpy (_address, address),
X	      strcpy (_line, line);
X	    if (address [0] != EOS && re_strcmp (_address, _line, NULL) > 0) {
X	      /* User found; remove him */
X	      char _address [MAX_LINE];
X	      BOOLEAN removed = FALSE;
X
X	      fclose (subscribers);
X	      sprintf (_address, "^[\t ]*%s ", address);
X	      if (cp (subscribersf, OLD_SUBSCRIBERS))
X		exit (16);
X	      REMOVE_ADDRESS (_address, OLD_SUBSCRIBERS, NEW_SUBSCRIBERS,
X			      removed_usersf);
X# ifdef bsd
X	      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X	      sighold (SIGINT);
X	      sighold (SIGTERM);
X# endif
X	      if (mv (NEW_SUBSCRIBERS, subscribersf))
X		exit (16);
X# ifdef bsd
X	      sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	      sigrelse (SIGINT);
X	      sigrelse (SIGTERM);
X# endif
X	      OPEN_FILE (subscribers, subscribersf, "r", "process_message");
X	      if (cp (aliasesf, OLD_ALIASES))
X		exit (16);
X	      REMOVE_ADDRESS (_address, OLD_ALIASES, NEW_ALIASES,
X			      removed_aliasesf);
X# ifdef bsd
X	      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X	      sighold (SIGINT);
X	      sighold (SIGTERM);
X# endif
X	      if (mv (NEW_ALIASES, aliasesf))
X		exit (16);
X# ifdef bsd
X	      sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	      sigrelse (SIGINT);
X	      sigrelse (SIGTERM);
X# endif
X	      if (cp (ALIASESF, OLD_ALIASES))
X		exit (16);
X	      REMOVE_ADDRESS (_address, OLD_ALIASES, NEW_ALIASES, "/dev/null");
X# ifdef bsd
X	      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X	      sighold (SIGINT);
X	      sighold (SIGTERM);
X# endif
X	      if (mv (NEW_ALIASES, ALIASESF))
X		exit (16);
X# ifdef bsd
X	      sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	      sigrelse (SIGINT);
X	      sigrelse (SIGTERM);
X# endif
X	      sprintf (_address, " %s[\t ]*$", address);
X	      if (cp (aliasesf, OLD_ALIASES))
X		exit (16);
X	      REMOVE_ADDRESS (_address, OLD_ALIASES, NEW_ALIASES, 
X			      removed_aliasesf);
X# ifdef bsd
X	      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X	      sighold (SIGINT);
X	      sighold (SIGTERM);
X# endif
X	      if (mv (NEW_ALIASES, aliasesf))
X		exit (16);
X# ifdef bsd
X	      sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	      sigrelse (SIGINT);
X	      sigrelse (SIGTERM);
X# endif
X	      if (cp (ALIASESF, OLD_ALIASES))
X		exit (16);
X	      REMOVE_ADDRESS (_address, OLD_ALIASES, NEW_ALIASES, "/dev/null");
X# ifdef bsd
X	      sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X	      sighold (SIGINT);
X	      sighold (SIGTERM);
X# endif
X	      if (mv (NEW_ALIASES, ALIASESF))
X		exit (16);
X# ifdef bsd
X	      sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	      sigrelse (SIGINT);
X	      sigrelse (SIGTERM);
X# endif
X	      unlink (OLD_SUBSCRIBERS);
X	      unlink (OLD_ALIASES);
X	      if (removed)
X		fprintf (forwardmail, "Automatic removal: user %s\n\
X  (possibly mentioned below) will be removed from the system files.\n", address);
X	      else {
X		fprintf (forwardmail, "Automatic removal of user %s\n  failed.\n",
X			 address_copy);
X		break;
X	      }
X	    }
X	  }
X	} 
X
X	strcpy (matches, ".*\\1");
X	if (re_strcmp (warnings, line, matches) > 0) {
X	  FILE *inerrors, *outerrors;
X	  BOOLEAN found = FALSE;
X	  char _line [1024], address [MAX_LINE], _address [MAX_LINE], *sp;
X	  long int stime;
X
X	  mv (errorsf, errors2f);
X	  inerrors = fopen (errors2f, "r");
X	  OPEN_FILE (outerrors, errorsf, "w", "process_message");
X	  while (inerrors && !feof (inerrors)) {
X	    RESET (_line);
X	    fgets (_line, 1023, inerrors);
X	    sscanf (_line, "%ld", &stime);
X	    if ((sp = strchr (_line, ' ')))
X	      ++sp;
X	    else
X	      sp = _line;
X	    upcase (sp);
X	    if (!strcmp (line, sp)) { /* Match */
X	      found = TRUE;
X	      if ((long int) time (0) < (long int) (stime + GRACE_PERIOD))
X		/* No action */
X		fputs (_line, outerrors);
X	      else {	/* Look for matches in the subscribers file and */
X		char mode [MAX_LINE], address_copy [MAX_LINE], *p;
X
X		rewind (subscribers); /* change mail mode to POSTPONE */
X		while (!feof (subscribers)) {
X		  mode [0] = address [0] = RESET (_line);
X		  fgets (_line, MAX_LINE - 2, subscribers);
X		  sscanf (_line, "%s %s", address, mode);
X		  if (!strcmp (mode, POSTPONE))
X		    continue;
X		  upcase (_line);
X		  upcase (address);
X		  strcpy (address_copy, address);
X		  escape_re (address);
X# if (ERROR_MAIL_ANALYSIS == 9)
X		  if (strcmp (matches, ".*") && strcmp (matches, ".*\\1"))
X		    sprintf (_address, "^[ \t]*%s", matches + 2);
X		  else
X# endif
X		    strcpy (_address, address),
X		    strcpy (_line, line);
X		  if (p = strchr (_address, '<'))
X		    sprintf (p, "%s", p + 1); /* Remove '<' */
X		  if (p = strchr (_address, '>'))
X		    sprintf (p, "%s", p + 1);  /* Remove '>' */
X		  if (address [0] != EOS && re_strcmp (_address, _line, NULL) >
X		      0) {
X		    /* User found; change mode */
X		    char _address [MAX_LINE];
X		    BOOLEAN suspended = FALSE;
X
X		    fclose (subscribers);
X		    sprintf (_address, "^[\t ]*%s ", address);
X		    if (cp (subscribersf, OLD_SUBSCRIBERS))
X		      exit (16);
X		    SUSPEND_ADDRESS (_address, OLD_SUBSCRIBERS, NEW_SUBSCRIBERS);
X# ifdef bsd
X		    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X# elif defined (svr4) || defined (svr3)
X		    sighold (SIGINT);
X		    sighold (SIGTERM);
X# endif
X		    if (mv (NEW_SUBSCRIBERS, subscribersf))
X		      exit (16);
X# ifdef bsd
X		    sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X		    sigrelse (SIGINT);
X		    sigrelse (SIGTERM);
X# endif
X		    OPEN_FILE (subscribers, subscribersf, "r", "process_message");
X		    if (suspended)
X		      fprintf (forwardmail, "Subscription subspension: user \
X%s\n  (possibly mentioned below) will have his mail mode reset to POSTPONE.\n",
X			       address_copy);
X		    else {
X		      fprintf (forwardmail, "Attempt to suspend subscription \
Xof user %s\n  failed.\n", address_copy);
X		      break;
X		    }
X		  }
X		}
X              }
X	    }
X	    else if ((long int) time (0) < (long int) (stime + ONE_MONTH))
X	      /* discard if older > 1 mo */
X	      fputs (_line, outerrors); /* No match; copy error line as is */
X	  }
X	  if (!found)	/* Save new error message */
X	    fprintf (outerrors, "%ld %s", time (0), line),
X	    fprintf (forwardmail, "Recorded new error message: %s", line);
X	  fclose (outerrors);
X	  if (inerrors)
X	    fclose (inerrors);
X	  unlink (errors2f);
X	}
X      }
X    }
X#endif
X
X    fprintf (forwardmail, "-----------------------------------------------\
X--------------------------------\n");
X    rewind (msg);
X    while (!feof (msg))  /* Copy actual message */
X      RESET (line),
X      fgets (line, MAX_LINE - 2, msg),
X      fprintf (forwardmail, "%s", line);
X    COMPLETE_TELNET (forwardmail);
X    fclose (forwardmail);
X    fclose (msg);
X    /* Form UNIX sendmail command */
X    if (sender_subscribed != PEER && sender_subscribed != NEWS)
X      if (sys.options & USE_SYSMAIL)
X	sysmail (mailforwardf);
X      else
X	syscom ("%s '%s' < %s", sys.mail.method,
X		(((sys.options & USE_TELNET) == 0) ?
X		 ((mailer_daemon || susp_subject || errors_to_owner) ?
X		  sys.lists[listid].owner : locase (sender)) : ""),
X		mailforwardf);
X    return;
X  }
X  if (is_moderated) /* USER CONTRIBUTED CODE: Warren Burstein */
X    if (owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X      /* 
X	 Sender is owner, look for MESSAGE SEPARATOR, subject and sender,
X	 and blank lines and set lines_to_skip (# of lines till 
X	 MESSAGE_SEPARATOR + one for Subject: + one for Sender: + blank lines)
X      */
X      FILE *msg;
X      long int line_no = 0;
X
X      OPEN_FILE (msg, msgf, "r", "process_message");
X      while (!feof (msg)) {
X	RESET (line);
X	fgets (line, MAX_LINE - 2, msg);
X	if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X	  line [strlen (line) - 1] = EOS;
X	line_no++;
X
X	if (!strcmp (line, MESSAGE_SEPARATOR)) {
X	  lines_to_skip = line_no;
X	  break;
X	}
X      }
X
X      if (lines_to_skip) {
X	/* Look for Subject: and Sender:, skip blank lines */
X	char match [MAX_LINE];
X	while (!feof (msg)) {
X	  RESET (line);
X	  fgets (line, MAX_LINE - 2, msg);
X	  if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X	    line [strlen (line) - 1] = EOS;
X
X	  strcpy (match, "\\1");
X	  if (re_strcmp (SUBJECT, line, match) > 0)
X	    strcpy (senders_subject, line + strlen (match)),
X	    strcpy (subject_copy, senders_subject),
X	    lines_to_skip++;
X	  else if (re_strcmp (SENDER, line, match) > 0)
X	    strcpy (original_sender, line + strlen (match)),
X	    lines_to_skip++;
X	  else if (line[0] == EOS) {
X	    lines_to_skip++;	/* [Tasos: ???] */
X	    break;
X	  }
X	  else
X	    break;
X        }
X        while (!feof (msg)) {
X	  RESET (line);
X	  fgets (line, MAX_LINE - 2, msg);
X	  if (strcmp (line, "\n"))
X	    break;
X	  lines_to_skip++;
X        }
X      }
X
X      fclose (msg);
X    }
X    else { /* Sender isn't owner, send message to owner */
X      int i;
X
X      report_progress (report, tsprintf ("Public message #%04d. Forwarding to \
Xmoderator", ++public_msg), TRUE);
X      OPEN_FILE (msg_no, msg_nof, "w", "process_message");
X      fprintf (msg_no, "%d %d\n", public_msg, returned_msg);
X      fclose (msg_no);
X
X      create_header (&forwardmail, mailforwardf, original_sender,
X		     originator, sys.lists[listid].owner, senders_subject,
X		     FALSE, TRUE, FALSE);
X
X      sprintf (line, "This message was submitted by %s to list %s.",
X	       original_sender, sys.lists[listid].address);
X      fill_text (forwardmail, line);
X      for (i = 0; msgs[i]; i++)
X	fill_text (forwardmail, msgs[i]);
X      fprintf (forwardmail, "%s\n", MESSAGE_SEPARATOR);
X
X      /* Send these two under the line for extraction when the moderator
X	 forwards the message back */
X      fprintf (forwardmail, "Sender: %s\nSubject: %s\n\n",
X	       original_sender, senders_subject);
X      if (copy_msg (forwardmail, msgf, FALSE, original_sender, 0, TRUE))
X        sendmail (sys.lists[listid].owner, FALSE, FALSE, 0, 0, mailforwardf);
X      already_sent = message_forwarded = TRUE;
X    }
X  fclose (msg);
X  
X  if (!already_sent) {
X    report_progress (report,
X		     tsprintf ("Public message #%04d%s. Distributing to all \
Xsubscribers%s.\n%s",
X			       ++public_msg,
X			       (sender_subscribed == NEWS ? " (news)" : 
X				(sender_subscribed == PEER ? " (peer)" : "")), 
X			       (sender_subscribed == SUBSCRIBED ? 
X				", news groups and peers" : 
X				(sender_subscribed == NEWS ? ", peers" : 
X				 ", news groups")),
X			       sender), TRUE);
X    if (fake_mail && at)
X      report_progress (report,
X		       tsprintf ("*** Possible fake mail: Host %s in user \
Xaddress does\nnot match any of the domains in the Received: header lines.\n",
X				 at + 1), FALSE);
X    OPEN_FILE (msg_no, msg_nof, "w", "process_message");
X    fprintf (msg_no, "%d %d\n", public_msg, returned_msg);
X    fclose (msg_no);
X    distributions (sender_subscribed, headerf, msgf, FALSE, sender,
X		   original_sender, senders_subject, subject_copy, originator,
X		   lines_to_skip, is_moderated);
X
X    if (append_to_digest && !message_rejected) {
X      long int line_no = 0, digest_toc_lines, digest_lines, total;
X      FILE *fp, *msg;
X
X      OPEN_FILE (fp, digest_tocf, "a", "process_message");
X      fprintf (fp, "%s (%s)\n", senders_subject, original_sender);
X      fclose (fp);
X
X      OPEN_FILE (fp, digest_msgf, "a", "process_message");
X      if (sent_date[0] != EOS)
X	fprintf (fp, "\nDate: %s", sent_date);
X      fprintf (fp, "\nFrom: %s\nSubject: %s\n\n", original_sender,
X	       senders_subject);
X
X      OPEN_FILE (msg, msgf, "r", "process_message");
X      while (!feof (msg)) { /* Copy actual message */
X	RESET (line);
X	fgets (line, MAX_LINE - 2, msg);
X	line_no++;
X
X	if (line_no > lines_to_skip)
X	  fprintf (fp, "%s", line);
X      }
X      fclose(msg);
X
X      fprintf (fp, "\n------------------------------\n");
X      fclose(fp);
X
X      digest_toc_lines = count_lines_in_file (fp = fopen (digest_tocf, "r"));
X      if (fp) fclose(fp);
X      digest_lines = count_lines_in_file (fp = fopen (digest_msgf, "r"));
X      if (fp) fclose(fp);
X
X      report_progress (report, tsprintf ("Adding to digest, which now has \
X%d token + %d message = %d lines",
X               digest_toc_lines, digest_lines,
X               (total = digest_toc_lines + digest_lines)), TRUE);
X
X      if (total >= sys.lists[listid].digest_lines) { /* Max lines exceeded */
X	BOOLEAN fd = force_digest;
X
X	force_digest = TRUE;
X        digest_make (TRUE);
X        digest_distribute ();
X	force_digest = fd;
X      }
X    }
X  }
X
X  update_unprocessed_messages (unprocessed_messages);
X  unlink (unprocessed_tmp);
X
X  if (!message_rejected && !message_forwarded) { /* New message is archived */
X    cat_append (headerf, archivef);
X    OPEN_FILE (in, msgf, "r", "process_message");
X    OPEN_FILE (out, archivef, "a", "process_message");
X    if (is_moderated) { /* Skip message lines before appending */
X      long int offset = ftell (in); /* Store current position */
X      while (!feof (in)) {
X	RESET (line);
X	fgets (line, MAX_LINE - 2, in);
X	if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X	  line [strlen (line) - 1] = EOS;
X	if (!strcmp (line, MESSAGE_SEPARATOR))
X	  break;
X      }
X      if (feof (in))	/* No message separator, so archive the whole msg */
X	(void) fseek (in, offset, SEEK_SET);
X    }
X    while (!feof (in))
X      RESET (line),
X      fgets (line, MAX_LINE - 2, in),
X      fputs (line, out);
X    fclose (in);
X    fclose (out);
X    unlink (headerf);
X    if (is_moderated) /* Save the original author's address */
X      extract_address (original_sender),
X      upcase (original_sender),
X      fprintf (headers, "%s\n", original_sender);
X    else
X      fprintf (headers, "%s\n", sender);
X    fflush (headers);
X  }
X
X  if (message_id[0] != EOS && !message_rejected && !message_forwarded) {
X    if ((message_ids = fopen (message_idsf, "a")) == NULL)
X      report_progress (report,
X		       tsprintf ("\nprocess_message(): Could not open %s",
X				 message_idsf), TRUE),
X      gexit (1);
X    fprintf (message_ids, "%s %s\n", message_id, sender); /* Save new id */
X    fclose (message_ids);
X  }
X}
X
X/*
X  Check to see if there are subscribers/newsgroups/peers that did not
X  receive any mail because of a previous interrupted distribution.
X*/
X
XBOOLEAN unprocessed_users (char *unprocessed, char *s, FILE *report)
X{
X  struct stat stat_buf;
X
X  if (!stat (unprocessed, &stat_buf)) { /* There is undelivered mail */
X    if (stat_buf.st_size > 0)
X      report_progress (report,
X		       tsprintf ("Resuming delivery of interrupted mail to %s",
X				 s), TRUE);
X    return TRUE;
X  }
X  return FALSE;
X}
X
X/*
X  Distribute a message, either to regular subscribers, or to DIGEST
X  subscribers if is_digest is TRUE.
X  Write an archive file if is_digest == sys.lists[listid].options & ARCHIVE_DIGEST
X*/
X
Xvoid distributions (BOOLEAN subscribed, char *header_file,
X		    char *message_file, BOOLEAN is_digest,
X		    char *sender, char *original_sender, char *senders_subject,
X		    char *subject_copy, char *originator,
X		    long int lines_to_skip, BOOLEAN is_moderated)
X{
X  BOOLEAN sremnants, nremnants, premnants;
X
X  if (!(sremnants = unprocessed_users (unprocessed_subscribersf,
X				       "subscribers", report)))
X    echo ("0", unprocessed_subscribersf);
X  if (!(nremnants = unprocessed_users (unprocessed_newsf, "news", report)))
X    echo ("0", unprocessed_newsf);
X  if (!(premnants = unprocessed_users (unprocessed_peersf, "peers", report)))
X    echo ("0", unprocessed_peersf);
X
X  do_distribute (message_file, subscribers, subscribersf, sremnants,
X		 unprocessed_subscribersf, send_to_subscribers, sender,
X		 original_sender, senders_subject, subject_copy, originator,
X		 report, FALSE, multi_recip, lines_to_skip, is_digest, TRUE,
X		 subscribed);
X  unlink (unprocessed_subscribersf);
X
X  if ((subscribed == SUBSCRIBED || subscribed == PEER) && !is_digest)
X    /* Send to news also */
X    do_distribute (message_file, news, newsf, nremnants, unprocessed_newsf,
X		   TRUE, sender, original_sender, senders_subject,
X		   subject_copy, originator, report, TRUE, FALSE,
X		   lines_to_skip, is_digest, FALSE, subscribed);
X  unlink (unprocessed_newsf);
X
X  if ((subscribed == SUBSCRIBED || subscribed == NEWS) && !is_digest)
X    /* Send to peers also */
X    do_distribute (message_file, peers, peersf, premnants,
X		   unprocessed_peersf, TRUE, sender, original_sender, 
X		   senders_subject, subject_copy, originator, 
X		   report, FALSE, FALSE, lines_to_skip, is_digest, FALSE,
X		   subscribed);
X  unlink (unprocessed_peersf);
X
X  if (((sys.lists[listid].options & ARCHIVE_LIST) &&
X       !(sys.lists[listid].options & ARCHIVE_DIGEST) && !is_digest) ||
X      (is_digest & sys.lists[listid].options & ARCHIVE_DIGEST))
X    do_archive (header_file, message_file, subject_copy, is_moderated);
X}
X
X/*
X  Do the distribution of the message to the specified list of subscribers.
X  The BOOLEAN argument 'posting' should be TRUE when posting AND gating to
X  news, and FALSE otherwise.
X
X  Undelivered mail has higher priority.
X*/
X
Xvoid do_distribute (char *message_file,
X		    FILE *recipient_list, char *file, BOOLEAN mail_remnants,
X		    char *unprocessed, BOOLEAN send_to_subscribers, 
X		    char *sender, char *original_sender, char *senders_subject,
X		    char *subject_copy, char *originator, FILE *report,
X		    BOOLEAN posting, BOOLEAN multi_recip,
X		    long int lines_to_skip, BOOLEAN is_digest,
X		    BOOLEAN is_subscribers, BOOLEAN subscribed)
X{
X  char name [MAX_LINE];            /* holds each subscribers name */
X  char mailmode [MAX_LINE];        /* send message back to sender or not */
X  char subscriber [MAX_LINE];      /* holds each subscriber's address */
X  char recipient_file [MAX_LINE];  /* file of subscribers to use when -r */
X  char restricted_subscriber [MAX_LINE]; /* name of subscriber in RESTRICTED */
X  char line [MAX_LINE];
X  FILE *forwardmail, *f;
X  BOOLEAN alternate_file = FALSE;
X  int  nrecipients = 0, offset = -1;
X
X  /* Check if there is previously undelivered mail */
X  if (mail_remnants) { /* There is undelivered mail; move to offset */
X    OPEN_FILE (f, unprocessed, "r", "do_distribute");
X    fscanf (f, "%d", &offset);
X    fclose (f);
X    if (offset < 0) /* Empty unprocessed file, go to eof */
X      while (!feof (recipient_list))
X	fgets (line, MAX_LINE - 2, recipient_list);
X    else /* Skip 'offset' subscribers, check for DIGEST subscribers */
X      while (!feof (recipient_list) && offset) {
X	if (!is_digest &&
X	    read_recipient (recipient_list, subscriber, mailmode, name,
X			    is_subscribers) && !strcmp (mailmode, DIGEST))
X	  append_to_digest = TRUE;
X	--offset;
X      }
X  }
X  else {
X    if (!send_to_subscribers) {  /* -r: Check if restricted mail has arrived */
X      rewind (restricted);
X      while (!feof (restricted)) {
X        alternate_file = TRUE;
X        restricted_subscriber[0] = RESET (recipient_file);
X        fscanf (restricted, "%s %s", restricted_subscriber, recipient_file);
X        upcase (restricted_subscriber);
X        if (!strcmp (restricted_subscriber, sender)) {
X  	  if (!strcmp (recipient_file, NO_RECIPIENT_FILE)) {
X	    /* Do not forward to anyone */
X	    report_progress (report, "No alternate recipient file specified. \
XMessage ignored.\n", FALSE);
X	    return;
X	  }
X	  /* Restricted mail; forward message only to the people listed
X	     in recipient_file */
X	  OPEN_FILE (recipient_list, recipient_file, "r", "do_distribute");
X	  report_progress (report, tsprintf ("Restricted mail. Distributing \
Xonly to individuals listed in %s", recipient_file), TRUE);
X	  break;
X        }
X      }
X    }
X  }
X  if (!mail_remnants)
X    rewind (recipient_list);
X  while (!feof (recipient_list)) { /* Send to all of them */
X
X    if (!read_recipient (recipient_list, subscriber, mailmode, name,
X			 is_subscribers))
X      continue;
X
X    if (is_digest) {
X      if (strcmp (mailmode, DIGEST))
X	continue;
X    }
X    else if (!strcmp (mailmode, DIGEST)) {
X      append_to_digest = TRUE;
X      continue;
X    }
X    else if (!strcmp (mailmode, NOACK)) {
X      upcase (subscriber);
X      if (!strcmp (subscriber, sender) || !strcmp (subscriber, originator))
X	continue;
X    }
X    else if (!strcmp (mailmode, POSTPONE))  /* No email to this subscriber */
X      continue;
X    else if (mailmode[0] != EOS && strcmp (mailmode, ACK)) {
X      syscom ("echo Unrecognized mail mode %s for recipient %s in %s | \
Xrmail %s %s",
X	      mailmode, subscriber, file, sys.manager, sys.lists[listid].owner);
X      report_progress (report, tsprintf ("\ndo_distribute(): Unrecognized mail \
Xmode %s in %s", mailmode, file), TRUE);
X      continue;
X    }
X    RESET (senders_subject);
X    strcpy (senders_subject, subject_copy);
X    if (multi_recip) {
X      if ((multi_recipients[nrecipients] =
X	   (char *) malloc ((strlen (subscriber) + 1) * sizeof (char)))
X	  == NULL)
X        report_progress (report, "\ndo_distribute(): malloc() failed", TRUE),
X	gexit (11);
X      strcpy (multi_recipients[nrecipients++], locase (subscriber));
X    }
X    /* Prepare header */
X    if (posting) {
X      if (sys.options & POST_MAIL) /* Post to news group */
X	create_news_header (&forwardmail, mailforwardf, original_sender,
X			    originator, name, senders_subject);
X      else if (sys.options & GATE_MAIL) /* Gate to news gateway */
X	create_gate_header (&forwardmail, mailforwardf, original_sender,
X			    originator, subscriber, name, senders_subject),
X	posting = FALSE;
X      if (!copy_msg (forwardmail, message_file, posting, original_sender,
X		     lines_to_skip, subscribed))
X	return;
X    }
X    else { /* Regular mail */
X      if (multi_recip) { /* Multi recipient header */
X	if (nrecipients == maxrecipients) {
X	  create_multi_recipient_header (&forwardmail, mailforwardf,
X					 original_sender, originator,
X					 tsprintf ("Multiple recipients of \
Xlist <%s>", sys.lists[listid].address),
X					 senders_subject, nrecipients);
X	  if (!copy_msg (forwardmail, message_file, posting, original_sender,
X			 lines_to_skip, subscribed))
X	    return;
X	}
X      }
X      else { /* Single recipient header */
X	create_header (&forwardmail, mailforwardf, original_sender, originator,
X		       subscriber, senders_subject, FALSE, FALSE, FALSE);
X	if (!copy_msg (forwardmail, message_file, posting, original_sender,
X		       lines_to_skip, subscribed))
X	  return;
X      }
X    }
X    sendmail (subscriber, posting, multi_recip, nrecipients, maxrecipients,
X	      mailforwardf);
X    update_unprocessed (unprocessed, posting, multi_recip, nrecipients);
X    if (nrecipients == maxrecipients)
X      nrecipients = 0;
X  }
X  if (alternate_file)  /* Close alternate recipient file */
X    fclose (recipient_list);
X
X  if (multi_recip && (nrecipients != 0)) { /* Left overs */
X    create_multi_recipient_header (&forwardmail, mailforwardf,
X				   original_sender, originator,
X				   tsprintf ("Multiple recipients of list <%s>",
X					     sys.lists[listid].address),
X				   senders_subject, nrecipients);
X    if (copy_msg (forwardmail, message_file, posting, original_sender,
X		  lines_to_skip, subscribed))
X      sendmail (subscriber, posting, multi_recip, nrecipients, nrecipients, 
X	        mailforwardf),
X      update_unprocessed (unprocessed, posting, multi_recip, nrecipients);
X  }
X}
X
X/*
X  Increment count of number of recipients that have received the current
X  message.
X*/
X
Xvoid update_unprocessed (char *unprocessed, BOOLEAN posting,
X			 BOOLEAN multi_recip, int nrecipients)
X{
X  int nproc = 1, offset;
X  FILE *f;
X  struct stat stat_buf;
X
X  if (stat (unprocessed, &stat_buf) || stat_buf.st_size <= 0)
X    return;
X  if (multi_recip && !posting)
X    nproc = nrecipients;
X  OPEN_FILE (f, unprocessed, "r", "update_unprocessed");
X  fscanf (f, "%d", &offset);
X  fclose (f);
X  OPEN_FILE (f, unprocessed, "w", "update_unprocessed");
X  fprintf (f, "%d\n", offset + nproc);
X  fclose (f);
X}
X
X/*
X  Remove the message that was just delivered to everybody from the
X  unprocessed file.
X*/
X
Xvoid update_unprocessed_messages (char *unprocessed)
X{
X  FILE *in, *out;
X  char line [MAX_LINE];
X
X  OPEN_FILE (in, unprocessed, "r", "update_unprocessed_messages");
X  OPEN_FILE (out, unprocessed_tmp, "w", "update_unprocessed_messages");
X  if (!feof (in))
X    fgets (line, MAX_LINE - 2, in); /* Skip first "From " */
X  RESET (line);
X  while (!feof (in) &&
X         (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE))))
X    fgets (line, MAX_LINE - 2, in);
X  while (!feof (in)) /* Copy rest of messages */
X    fputs (line, out),
X    RESET (line),
X    fgets (line, MAX_LINE - 2, in);
X  fclose (in);
X  fclose (out);
X  mv (unprocessed_tmp, unprocessed);
X}
X
X/*
X  Copy the original message from message_file to the outgoing message.
X  Skip first "skip" lines. Reject it if it messages have a certain
X  limit, and that limit is exceeded (only when not distributing a digest).
X*/
X
XBOOLEAN copy_msg (FILE *forwardmail, char *message_file, BOOLEAN posting,
X		  char *sender, long int lines_to_skip, BOOLEAN subscribed)
X{
X  FILE *msg;
X  char line [MAX_LINE];
X  long int count = 0, line_no = 0;
X
X  OPEN_FILE (msg, message_file, "r", "copy_msg");
X  while (!feof (msg)) { /* Copy actual message */
X    RESET (line);
X    fgets (line, MAX_LINE - 2, msg);
X    line_no++;
X
X    if (line_no > lines_to_skip)
X      fprintf (forwardmail, "%s", line);
X
X    count += strlen (line);
X    message_rejected = FALSE;
X    if ((sys.options & LIMIT_MSG) && (count > sys.limits.msg) &&
X	subscribed != FALSE && subscribed != PEER && subscribed != NEWS) {
X      fclose (forwardmail);
X      rewind (msg);
X      create_header (&forwardmail, mailforwardf, sys.lists[listid].address, 
X		     sys.lists[listid].address, sender, "Posting rejected",
X		     COPY_OWNER (ccerrors), FALSE, TRUE);
X      fprintf (forwardmail, "Your posting to list %s was rejected.\n\
XReason: message size limit exceeded (maximum allowed: %ld bytes)\n\
XThe first few lines of your message are included herein for reference:\n\n",
X	       sys.lists[listid].alias, sys.limits.msg);
X      report_progress (report, "Message rejected: maximum size exceeded",
X		       TRUE);
X      message_rejected = TRUE;
X      count = 0;
X      while (!feof (msg) && count < 10)
X	RESET (line),
X	fgets (line, MAX_LINE - 2, msg),
X	fprintf (forwardmail, "%s", line),
X	++count;
X      COMPLETE_TELNET (forwardmail);
X      fclose (forwardmail);
X      fclose (msg);
X      sendmail (sender, FALSE, FALSE, 0, 0, mailforwardf);
X      return FALSE;
X    }
X  }
X  if (!posting && (sys.options & USE_TELNET))
X    COMPLETE_FILE (forwardmail);
X  fclose (forwardmail);
X  fclose (msg);
X  return TRUE;
X}
X
X/*
X  Send email (to subscribers/news/gateways/peers). It may send one single
X  copy to one recipient, or one copy to multiple recipients.
X*/
X
Xvoid sendmail (char *subscriber, BOOLEAN posting, BOOLEAN multi_recip,
X	       int nrecipients, int maxrecipients, char *mailforwardf)
X{
X  char *buf;
X  int size, i;
X
X  /* Form UNIX sendmail command */
X  if (posting && (sys.options & POST_MAIL)) { /* Post to news */
X    if (syscom ("%s -h < %s > %s 2> %s", INEWS, mailforwardf, INEWS_REPLY,
X		INEWS_REPLY)) {
X      FILE *f = fopen (mailforwardf, "r");
X      char line [MAX_LINE], match [MAX_LINE];
X      while (f && !feof (f)) {
X	RESET (line);
X	fgets (line, MAX_LINE - 2, f);
X	strcpy (match, "\\1");
X	if (re_strcmp (FROM, line, match) > 0) {
X	  sprintf (line, "%s", line + strlen (match)); /* Save From: */
X	  line [strlen (line) - 1] = EOS; /* \n -> \0 */
X	  extract_address (line);
X	  break;
X	}
X      }
X      if (f)
X	fclose (f);
X      syscom ("%s -s \"Posting to `grep '^Newsgroups:' %s | \
Xawk '{ print $2 }'` failed: \" %s %s < %s",
X	      UCB_MAIL, mailforwardf, line, sys.lists[listid].owner,
X	      INEWS_REPLY);
X      unlink (INEWS_REPLY);
X    }
X  }
X  else { /* Send email */
X    if (multi_recip) { 
X      if (nrecipients == maxrecipients) {
X	if ((sys.options & USE_TELNET) == 0) { /* Line up recipients */
X	  size = strlen (multi_recipients[0]) + 1;
X	  if ((buf = (char *) malloc (size * sizeof (char))) == NULL)
X	    report_progress (report, "\nsendmail(): malloc() failed", TRUE),
X	    gexit (11);
X	  strcpy (buf, multi_recipients[0]);
X	  for (i = 1; i < nrecipients; i++)
X	    size += strlen (multi_recipients[i]) + 2,
X	    buf = (char *) realloc (buf, size * sizeof (char)),
X	    sprintf (buf + strlen (buf), " %s", multi_recipients[i]);
X	  if ((int) strlen (buf) > 10000)
X	    report_progress (report, "Too many multiple recipients", TRUE),
X	    gexit (9);
X	  syscom ("%s '%s' < %s", sys.mail.method, buf, mailforwardf);
X	  free ((char *) buf);
X	}
X	else if (sys.options & USE_SYSMAIL)
X	  sysmail (mailforwardf);
X	else /* telnet */
X	  syscom ("%s < %s", sys.mail.method, mailforwardf);
X      }
X    }
X    else  /* Single recipient */
X      if (sys.options & USE_SYSMAIL)
X	sysmail (mailforwardf);
X      else
X	extract_address (locase (subscriber)),
X        syscom ("%s '%s' < %s", sys.mail.method,
X                (((sys.options & USE_TELNET) == 0) ? subscriber : ""),
X                mailforwardf);
X  }
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: list <-L LIST_ALIAS> [-r] [-m #] [-1] [-f] [-e] \
X[-s] [-p] [-P] [-v] [-M] [-d] [-i address] [-D]\n\
X-L: Process LIST_ALIAS (LIST_ALIAS has to be all in capital letters).\n\
X-r: Restricted mail.\n\
X-m: Specify number of multiple recipients in one outgoing message.\n\
X-1: Execute only once.\n\
X-f: Forward rejected messages to the list's owner.\n\
X-e: Echo reports to the screen.\n\
X-s: Do not check for subscriptions.\n\
X-p: Replies to posted (to news) messages will go to the author.\n\
X-P: Replies to distributed messages will go to the author.\n\
X-v: Version number.\n\
X-M: Moderated list.\n\
X-d: Distribute digest.\n\
X-i: Send partial digest to 'address'.\n\
X-D: Turn debug on.\n");
X  exit (3);
X}
X
Xvoid list_config (char *alias)
X{
X  if (!alias)
X    return;
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (headersf, alias, HEADERS);
X  setup_string (restrictedf, alias, RESTRICTED);
X  setup_string (newsf, alias, NEWSF);
X  setup_string (peersf, alias, PEERS);
X  setup_string (aliasesf, alias, ALIASES);
X  setup_string (ignoredf, alias, IGNORED);
X  setup_string (list_mail_f, alias, LIST_MAIL_FILE);
X  setup_string (list_moderated_f, alias, LIST_MODERATED_F);
X  setup_string (mboxf, alias, MBOX);
X  setup_string (msg_nof, alias, MSG_NO);
X  setup_string (mailforwardf, alias, MAILFORWARD);
X  setup_string (msgf, alias, MSG);
X  setup_string (archivef, alias, ARCHIVE);
X  setup_string (digest_nof, alias, DIGEST_NO);
X  setup_string (digest_tocf, alias, DIGEST_TOC);
X  setup_string (digest_msgf, alias, DIGEST_MSG);
X  setup_string (digest_timef, alias, DIGEST_TIME);
X  setup_string (headerf, alias, HEADER);
X  setup_string (mail_copyf, alias, MAIL_COPY);
X  setup_string (report_listf, alias, REPORT_LIST);
X  setup_string (message_idsf, alias, MESSAGE_IDS_F);
X  setup_string (unprocessed_digestf, alias, UNPROC_DIGEST);
X  setup_string (unprocessed_subscribersf, alias, UNPROC_SUBSCRIBERS);
X  setup_string (unprocessed_peersf, alias, UNPROC_PEERS);
X  setup_string (unprocessed_newsf, alias, UNPROC_NEWS);
X  setup_string (unprocessed_messages, alias, UNPROC_MESSAGES);
X  setup_string (unprocessed_tmp, alias, UNPROC_TMP);
X  setup_string (removed_usersf, alias, REMOVED_USERS);
X  setup_string (removed_aliasesf, alias, REMOVED_ALIASES);
X  setup_string (limitsf, alias, LIST_LIMITS);
X  setup_string (errorsf, alias, ERRORSF);
X  setup_string (errors2f, alias, ERRORS2F);
X}
X
Xvoid version ()
X{
X  fprintf (stderr, "%s\n", VERSION);
X  exit (0);
X}
X
X/*
X  Graceful exit. Remove pid file.
X*/
X
Xint gexit (int exitcode)
X{
X  unlink (PID_LIST);
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X#endif
X  exit (exitcode);
X}
X
X/*
X  Write the text to fp, filling to 80 characters. Writing a null string
X  flushes the paragraph.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
X#define LEN 79			/* to avoid wrapping on some terminals */
X
Xvoid fill_text (FILE *fp, char *s)
X{
X  static char buf [LEN + 1];
X  static int pos = 0;
X  char word [LEN + 1], *p;
X
X  if (*s == EOS) { /* Flush buffer */
X    if (pos)
X      fprintf (fp, "%s\n", buf),
X      pos = 0;
X    fprintf (fp, "\n");
X  }
X  else
X    while (*s) { /* Get a word */
X      for (p = word; (*s != EOS) && !isspace (*s); s++, p++)
X	*p = *s;
X      *p = EOS;
X
X      /* Flush line if it won't fit */
X      if ((pos + strlen (word) + (pos != 0)) > LEN)
X	fprintf (fp, "%s\n", buf),
X	pos = 0;
X
X      /* Add word to line, precede with space if not first word */
X      if (pos)
X	buf[pos++] = ' ';
X      strcpy (&buf[pos], word);
X      pos += (int) strlen (word);
X      buf[pos] = EOS;
X
X      /* Skip trailing spaces */
X      while (isspace(*s))
X	s++;
X    }
X}
X
X/*
X  Read a line from the recipients file, fill subscriber, mailmode and name.
X  Return TRUE if it's OK, FALSE if no subscriber found.
X*/
X
XBOOLEAN read_recipient (FILE *fp, char *subscriber, char *mailmode, char *name,
X			BOOLEAN is_subscribers)
X{
X  char othermode [MAX_LINE];
X  int i;
X  FILE *f;
X
X  othermode[0] = mailmode[0] = name[0] = RESET (subscriber);
X  if (!extract_subscriber (fp, subscriber, FALSE)) {
X    fgets (othermode, MAX_LINE - 2, fp); /* Skip to eoln */
X    NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS(subscriber, "read_recipient", FALSE);
X    return FALSE;
X  }
X  if (subscriber[0] == EOS)
X    return FALSE;
X  fscanf (fp, "%s ", mailmode);
X
X  if (is_subscribers)
X    for (i = 1; i < (MAX_SET_OPTIONS - 1); i++) /* Skip unused options */
X      fscanf (fp, "%s ", othermode);
X 
X  fgets (name, MAX_LINE - 2, fp);
X  if (name [0] != EOS && name [strlen (name) - 1] == '\n')
X    name [strlen (name) - 1] = EOS;
X  upcase (mailmode);
X
X  return TRUE;
X}
X
X/*
X  Count the number of lines in a file.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xlong int count_lines_in_file (FILE *fp)
X{
X  long int lines = 0;
X  char line [MAX_LINE];
X
X  if (fp)
X    while (!feof (fp)) {
X      RESET (line);
X      fgets (line, MAX_LINE - 2, fp);
X      if (line[0] != EOS)
X	++lines;
X    }
X
X  return lines;
X}
X
X/*
X  Put the digest_toc and digest_msg files together to make a digest
X  in unprocessed_digestf.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid digest_make (BOOLEAN to_everyone)
X{
X  FILE *ofp, *ifp;
X  char line [MAX_LINE];
X
X  OPEN_FILE (ofp, (to_everyone ? unprocessed_digestf : msgf), "w",
X	     "digest_make");
X  fprintf (ofp, "Contents:\n");
X
X  OPEN_FILE (ifp, digest_tocf, "r", "digest_make");
X  while (!feof (ifp)) {
X    RESET (line);
X    fgets (line, MAX_LINE - 2, ifp);
X    if (line[0] != EOS)
X      fputs (line, ofp);
X  }
X  fclose (ifp);
X
X  fprintf (ofp, "\n------------------------------------------\
X----------------------------\n");
X
X  OPEN_FILE (ifp, digest_msgf, "r", "digest_make");
X  while (!feof (ifp)) {
X    RESET (line);
X    fgets (line, MAX_LINE - 2, ifp);
X    if (line[0] != EOS)
X      fputs (line, ofp);
X  }
X  fclose (ifp);
X
X  fprintf (ofp, "End of Digest\n************************\n");
X  fclose (ofp);
X
X  if (to_everyone) {
X    unlink (digest_tocf);
X    unlink (digest_msgf);
X    OPEN_FILE (ofp, digest_timef, "w", "digest_make");
X    fprintf (ofp, "%ld\n", time (0));
X    fclose (ofp);
X  }
X
X  report_progress (report,
X		   tsprintf ("%sDistributing %sdigest #%04d.", DIGEST_TIME_,
X			     (to_everyone ? "" : "partial "), ++digest_msg),
X		   TRUE);
X
X  if (to_everyone) {
X    OPEN_FILE (digest_no, digest_nof, "w", "digest_make");
X    fprintf (digest_no, "%d\n", digest_msg);
X    fclose (digest_no);
X  }
X}
X
X/*
X  Distribute a digest.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid digest_distribute (void)
X{
X  char subject1[MAX_LINE], subject2[MAX_LINE];
X  struct stat stat_buf;
X
X  sprintf (subject1, "%s digest %d", sys.lists[listid].alias, digest_msg);
X  strcpy (subject2, subject1);
X
X  if (!stat (unprocessed_digestf, &stat_buf) && stat_buf.st_size > 0)
X    distributions (SUBSCRIBED, NULL, unprocessed_digestf, ARCHIVE_DIGEST,
X		   sys.lists[listid].address,
X		   sys.lists[listid].address, subject1, subject2,
X		   sys.lists[listid].address, 0, FALSE);
X  unlink (unprocessed_digestf);
X}
X
X/*
X  Copy message_file to a file in arch_dir, whose name is specified
X  by arch_spec.
X 
X  arch_spec is interpreted like a printf format string.  The following
X  formats are recognized:
X 
X  %m month		01 - 12
X  %d day of month	01 - 31
X  %y year               00 - 99
X  %j julian date	001 - 366
X  %h month name	Jan - Dec
X  %a contents of Archive-Name header line (cannot be used if arching digests)
X  %# digest number (only use this if archiving digests)
X  %1 first non-blank line of file
X  %v volume number (extracted from Volume # Number # line)
X  %n issue number (extracted from Volume # Number # line)
X  %% the character %
X  any other characters are entered into the name of the file
X  example: arch.%y%m%d
X 
X  If there already is a file by that name, add additional characters
X  to make a unique file.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid do_archive (char *header_file, char *message_file, char *subject,
X		 BOOLEAN is_moderated)
X{
X  FILE *fp;
X  char fullpath [MAX_LINE], line [MAX_LINE], dirname [MAX_LINE],
X    report_msg [MAX_LINE], *src, *dst, *p, *filep;
X  int i, volume_num = -1, issue_num = -1;
X  BOOLEAN done_volume = FALSE;
X  long int time_is = 0;
X  struct tm *t;
X  struct stat stat_buf;
X
X  time (&time_is);
X  t = localtime (&time_is);
X
X  strcpy (fullpath, sys.lists[listid].arch_dir);
X  dst = fullpath + strlen (fullpath);
X  *dst++ = '/';
X  filep = dst;		/* start of file name */
X
X  for (src = sys.lists[listid].arch_spec; *src != EOS; src++) {
X    if (*src == '%') {
X      /* found a %, if next char is a known format specifier, print the
X	 appropriate value.  Otherwise just print the character, so %% prints
X	 one %. */
X      switch (*++src) {
X      case 'm':		/* month number */
X	sprintf (dst, "%02d", t->tm_mon + 1);
X	dst += 2;
X	break;
X      case 'd':		/* day of month */
X	sprintf (dst, "%02d", t->tm_mday);
X	dst += 2;
X	break;
X      case 'y':		/* 2 digits of year */
X	sprintf (dst, "%02d", t->tm_year % 100);
X	dst += 2;
X	break;
X      case 'j':		/* julian date */
X	sprintf (dst, "%03d", t->tm_yday + 1);
X	dst += 3;
X	break;
X      case 'h':		/* short name of month */
X	strcpy (dst, mname[t->tm_mon]);
X	dst += 3;
X	break;
X      case 'a':		/* contents of Archive-Name: header line */
X	if (archive_name[0] == EOS)
X	  if (!get_archive_name (message_file, archive_name)) {
X	    reject_archive ("Archive-Name: name missing", subject);
X	    return;
X	  }
X	strcpy (dst, archive_name);
X	dst += strlen (archive_name);
X	break;
X      case '#':		/* digest number */
X	sprintf (dst, "%d", digest_msg);
X	dst += strlen (dst);
X	break;
X      case '1':		/* 1st word of 1st nonblank line of message */
X	OPEN_FILE (fp, message_file, "r", "do_archive");
X	while (!feof (fp)) {
X	  RESET (line);
X	  fgets (line, MAX_LINE - 2, fp);
X	  if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X	    line [strlen (line) - 1] = EOS;
X	  /* Skip leading blanks, truncate string at blank. */
X	  for (p = line; *p && isspace (*p); p++);
X	  if (*p) {
X	    strcpy (archive_name, p);
X	    for (p = archive_name; *p != EOS && !isspace (*p); p++);
X	    *p = EOS;
X	    break;
X	  }
X	  locase (archive_name);
X	}
X	fclose (fp);		
X	strcpy (dst, archive_name);
X	dst += strlen (archive_name);
X	break;
X      case 'v':
X	if (!done_volume) {
X	  if (!get_volume (message_file, &volume_num, &issue_num)) {
X	    reject_archive ("Volume number missing", subject);
X	    return;
X	  }
X	  done_volume = TRUE;
X	}
X	sprintf (dst, "%d", volume_num);
X	dst += strlen (dst);
X	break;
X      case 'n':
X	if (!done_volume) {
X	  if (!get_volume (message_file, &volume_num, &issue_num)) {
X	    reject_archive ("Issue number missing", subject);
X	    return;
X	  }
X	  done_volume = TRUE;
X	}
X
X	sprintf (dst, "%d", issue_num);
X	dst += strlen(dst);
X	break;
X      default:
X	*dst++ = *src;
X      }
X    }
X    else
X      *dst++ = *src;
X  }
X  *dst = EOS;
X
X  if (filep == dst) { /* No name to archive */
X    reject_archive ("Archive name is blank", subject);
X    return;
X  }
X
X  if (re_strcmp ("/*\\.\\./", fullpath, NULL) > 0) {
X    /* Someone is trying to write an archive into a parent dir. */
X    reject_archive (tsprintf ("Archive file %s contains ..", fullpath),
X		    subject);
X    return;
X  }
X  if (re_strcmp ("/", archive_name, NULL)) {
X    reject_archive (tsprintf ("Archive file %s contains directory", archive_name),
X		    subject);
X    return;
X  }
X  for (i = 0; i < strlen (fullpath); i++)
X    if (!isalnum (fullpath[i]) && fullpath[i] != '.' && fullpath[i] != '/' &&
X	fullpath[i] != '@' && fullpath[i] != '#' && fullpath[i] != '%' &&
X	fullpath[i] != '-') {
X      reject_archive (tsprintf ("Archive file %s contains invalid character '%c'",
X	       fullpath, fullpath[i]), subject);
X      return;
X    }
X
X  /* Create archive dir if needed.  Can't use arch_dir, there might be
X     /'s in %1 or %a or arch_spec. */
X  strcpy (dirname, fullpath);
X  if (p = strrchr (dirname, '/'))
X    *p = EOS;
X  if (!mkdir1 (dirname, report_msg, mask)) {
X    reject_archive (report_msg, subject);
X    return;
X  }
X
X/*
X  If the file already exists, append .1, .2 and so on.
X  for (i = 1; access (fullpath, 0) == 0; i++)
X    sprintf (dst, ".%d", i);
X*/
X
X  report_progress (report, tsprintf ("Archiving in %s", fullpath), TRUE);
X
X  if (stat (fullpath, &stat_buf))	/* Compress previous archive files */
X    syscom ("compress %s/* > /dev/null 2>&1 < /dev/null", dirname),
X    syscom ("uncompress %s/%s.Z %s/%s.Z > /dev/null 2>&1 < /dev/null", dirname,
X	    DIRF, dirname, INDEX);
X  if (is_moderated) { /* Skip message lines before appending */
X    char line [MAX_LINE];
X    FILE *in, *out;
X    if (header_file)
X      cat_append (header_file, fullpath);
X    OPEN_FILE (in, message_file, "r", "do_archive");
X    OPEN_FILE (out, fullpath, "a", "do_archive");
X    while (!feof (in)) {
X      RESET (line);
X      fgets (line, MAX_LINE - 2, in);
X      if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X	line [strlen (line) - 1] = EOS;
X      if (!strcmp (line, MESSAGE_SEPARATOR))
X	break;
X    }
X    if (feof (in))	/* No message separator, so archive the whole msg */
X      rewind (in);
X    while (!feof (in))
X      RESET (line),
X      fgets (line, MAX_LINE - 2, in),
X      fputs (line, out);
X    fclose (in);
X    fclose (out);
X  }
X  else {
X    if (header_file)
X      cat_append (header_file, fullpath);
X    cat_append (message_file, fullpath);
X  }
X  chmod (fullpath, 0644 & (0644 ^ otoi (mask)));
X
X  if (*sys.lists[listid].farch_dir != EOS)
X    farch (sys.lists[listid].arch_dir, sys.lists[listid].farch_dir,
X	   sys.lists[listid].arch_pass, fullpath, filep, subject);
X}
X
X/*
X  Add the archive file to the DIR in farch_dir.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid farch (char *arch_dir, char *farch_dir, char *password, char *fullpath,
X	    char *file_part, char *subject)
X{
X  FILE *fin, *fout;
X  long int dummy;
X  char dir [MAX_LINE], file [MAX_LINE], dirf [MAX_LINE],
X  full_arch_dir [MAX_LINE], full_farch_dir [MAX_LINE], error [MAX_LINE],
X  desc [MAX_LINE], _file [MAX_LINE];
X  char *p;
X  struct stat stat_buf;
X  BOOLEAN continued = FALSE, found;
X
X  /* If file_part contains a directory, extract that into dir to append
X     to farch_dir and to arch_dir. */
X  strcpy (dir, file_part);
X  if (p = strrchr(dir, '/'))
X    strcpy (file, p + 1),
X    *p = EOS,
X    sprintf (full_arch_dir, "%s/%s", arch_dir, dir),
X    sprintf (full_farch_dir, "%s/%s", farch_dir, dir);
X  else
X    RESET (dir),
X    strcpy(file, file_part),
X    strcpy (full_arch_dir, arch_dir),
X    strcpy (full_farch_dir, farch_dir);
X  sprintf (dirf, "%s/%s/%s", ARCHIVE_DIR, full_farch_dir, DIRF);
X  if (!make_indexes (dirf, full_farch_dir, password, error, mask)) {
X    reject_archive (error, subject);
X    return;
X  }
X
X  sprintf (error, "%s.old", dirf);
X  mv (dirf, error);
X  OPEN_FILE (fin, error, "r", "farch");
X  OPEN_FILE (fout, dirf, "w", "farch");
X  stat (fullpath, &stat_buf);
X  found = FALSE;
X  while (!feof (fin)) { /* Get location and file-count of file */
X    RESET (_file);
X    fscanf (fin, "%s", _file);
X    if (_file[0] != EOS) {
X      locase (_file);
X      if (!strcmp (_file, file)) {
X	fprintf (fout, "%s 1 %ld", file, stat_buf.st_size);
X	fscanf (fin, "%d %d", &dummy, &dummy);
X	found = TRUE;
X	do { /* Get file description */
X	  RESET (desc);
X	  fgets (desc, MAX_LINE - 2, fin);
X	  fputs (desc, fout);
X	  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\n')
X	    desc[strlen (desc) - 1] = EOS;
X	  continued = FALSE;
X	  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X	    continued = TRUE;
X	} while (continued);
X      }
X      else {
X	fprintf (fout, "%s", _file);
X	do { /* Get file description */
X	  RESET (desc);
X	  fgets (desc, MAX_LINE - 2, fin);
X	  fputs (desc, fout);
X	  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\n')
X	    desc[strlen (desc) - 1] = EOS;
X	  continued = FALSE;
X	  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X	    continued = TRUE;
X	} while (continued);
X      }
X    }
X  }
X  if (!found)
X    fprintf (fout, "%s 1 %ld %s %s\n", file, stat_buf.st_size, full_arch_dir,
X	     subject);
X  fclose (fin);
X  fclose (fout);
X  unlink (error);
X  chmod (dirf, 0644 & (0644 ^ otoi (mask)));
X}
X
X/*
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid reject_archive (char *msg, char *subject)
X{
X  FILE *forwardmail = NULL;
X  char report_msg [MAX_LINE];
X
X  sprintf (report_msg, "Cannot archive: %s", msg);
X  report_progress (report, report_msg, TRUE);
X
X  create_header (&forwardmail, mailforwardf, sys.lists[listid].address,
X		 sys.lists[listid].address, sys.lists[listid].owner,
X		 subject, FALSE, TRUE, TRUE);
X  fprintf (forwardmail, "Cannot archive: %s\n", msg);
X  fprintf (forwardmail, "arch_dir: %s\n", sys.lists[listid].arch_dir);
X  fprintf (forwardmail, "arch_spec: %s\n", sys.lists[listid].arch_spec);
X  fprintf (forwardmail, "Here is the message that was not archived:\n\
X------------------------------------------------------------------------------\
X\n\n");
X
X  if (copy_msg (forwardmail, msgf, FALSE, sys.lists[listid].address, 0, FALSE))
X    sendmail (sys.lists[listid].owner, FALSE, FALSE, 0, 0,
X	      mailforwardf);
X}
X
X/*
X  Look for a line like "volume <number> number <number>" and store
X  numbers in volume_num and issue_num.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
XBOOLEAN get_volume (char *message_file, int *volume_num, int *issue_num)
X{
X  FILE *fp;
X  char line [MAX_LINE], *p;
X  BOOLEAN found = FALSE;
X
X  OPEN_FILE (fp, message_file, "r", "get_volume");
X  while (!feof (fp) && !found) {
X    RESET (line);
X    fgets (line, MAX_LINE - 2, fp);
X    if (line [0] != EOS && line [strlen (line) - 1] == '\n')
X      line [strlen (line) - 1] = EOS;
X
X    locase (line);
X
X    for (p = line; *p != EOS && isspace (*p); p++);
X    if (strncmp (p, "volume", strlen ("volume")))
X      continue;
X
X    for (p += strlen ("volume"); *p != EOS && isspace (*p); p++);
X    if (!isdigit (*p))
X      continue;
X
X    *volume_num = atoi (p);
X    for (; *p != EOS && isdigit (*p); p++);
X
X    for (; *p != EOS && isspace (*p); p++);
X    if (strncmp (p, "number", strlen ("number")))
X      continue;
X
X    for (p += strlen ("number"); *p != EOS && isspace (*p); p++);
X    if (!isdigit (*p))
X      continue;
X
X    *issue_num = atoi (p);
X    found = TRUE;
X  }
X
X  fclose (fp);
X  return found;
X}
X
X/*
X  Get the Archive-Name: if any.
X*/
X
XBOOLEAN get_archive_name (char *message_file, char *archive_name)
X{
X  FILE *fp;
X  char error [MAX_LINE], line[MAX_LINE];
X
X  OPEN_FILE (fp, message_file, "r", "get_archive_name");
X  while (!feof (fp)) {
X    RESET (line);
X    fgets (line, MAX_LINE - 2, fp);
X    locase (line);
X    clean_request (line);
X    if (re_strcmp (ARCHIVE_NAME, line, NULL) > 0) {
X      sscanf (line, "%s %s", error, archive_name);
X      fclose (fp);
X      return TRUE;
X    }
X  }
X  fclose (fp);
X  return FALSE;
X}
X
X/*
X  Check if the daily limit of messages posted has been exceeded and return
X  immediately; otherwise, distribute no more than the limit minus messages
X  already posted and update the limits file.
X*/
X
XBOOLEAN limit_exceeded (char *limitsf, char *list_mail_f, char *mail_copyf)
X{
X  FILE *fin, *fout;
X  long int nmessages, mon, day, year, time_is;
X  struct tm *t;
X  char line [MAX_LINE];
X  char *tmprest;
X
X  nmessages = mon = day = year = 0;
X  if ((fin = fopen (limitsf, "r")))
X    fscanf (fin, "%d %d %d %d", &nmessages, &mon, &day, &year),
X    fclose (fin);
X  if (nmessages > sys.lists[listid].max_messages)
X    return TRUE;
X  OPEN_FILE (fin, list_mail_f, "r", "limit_exceeded");
X  OPEN_FILE (fout, mail_copyf, "w", "limit_exceeded");
X  while (!feof (fin) && nmessages < sys.lists[listid].max_messages) {
X    ++nmessages;
X    RESET (line);
X    fgets (line, MAX_LINE - 2, fin);
X    do {
X      fputs (line, fout);
X      RESET (line);
X      fgets (line, MAX_LINE - 2, fin);
X    } while (!feof (fin) &&
X	     (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE))));
X    if (!strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))
X      fseek (fin, -strlen (line), SEEK_CUR); /* Move back to beginning */
X  }
X  fclose (fout);
X  OPEN_FILE (fout, (tmprest = mystrdup (tmpnam (NULL))), "w", "limit_exceeded");
X  RESET (line);
X  while (!feof (fin))
X    RESET (line),
X    fgets (line, MAX_LINE - 2, fin),
X    fputs (line, fout);
X  fclose (fin);
X  fclose (fout);
X  mv (tmprest, list_mail_f);
X  free ((char *) tmprest);
X  time (&time_is);
X  t = localtime (&time_is);
X  echo (tsprintf ("%d %d %d %d", nmessages, t->tm_mon + 1, t->tm_mday,
X		  t->tm_year), limitsf);
X  return FALSE;
X}
*-*-END-of-src/list.c-*-*
echo x - src/listserv.c
sed 's/^X//' >src/listserv.c <<'*-*-END-of-src/listserv.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                         MAILING LIST SERVER                              |
X  |									     |
X  |                             Version 6.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.		     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  This is the system's server. People send requests to ListServer for
X  subscription, removal of subscription, general information request, etc.
X  In addition, list owners issue administrative requests.
X
X  The recognized user-commands are as follows:
X    help [command]
X    set <list> [<option> <arg[s]>]	option: mail, password, address, conceal
X					arg: ack, noack, postpone, digest
X					args: current-password new-password
X					args: current-password new-address
X					arg: yes, no
X    subscribe <list> <name>
X    unsubscribe <list> (alias signoff)
X    recipients <list> (alias review)
X    information <list>
X    statistics <list> {[subscriber email address(es)] | [-all]}
X    lists
X    which
X    index [archive | path-to-archive] [/password] [-all]
X    get <archive | path-to-archive> <filename> [/password] [parts]
X    search <archive | path-to-archive> [/password] [-all] <pattern>
X    fax <fax-number> <archive | path-to-archive> <filename> [/password] [parts]
X    run <list> [<password> cmd [args]]
X    release
X
X  List administration commands:
X    system <list> <password> <user-address> #<user-request>
X    reports <list> <password>
X    edit <list> <password> <file>
X    put <list> <password> <keyword> [args]
X    approve <list> <password> <tag>
X    discard <list> <password> <tag>
X
X  Manager oriented commands:
X    shutdown <password>
X    restart <password>
X    execute <password> #<prog> [args]
X
X  Above, <user-request> is any of the recognized user requests.
X
X  Commands are sent to ListServer one on each line of the message. The user
X  is notified of the first invalid request; all subsequent commands
X  are ignored -- imagine someone sending an entire book!! For each 
X  successfully completed command, a confirmation is sent back to the sender.
X  Commands/requests may be abbreviated.
X
X  USER ORIENTED REQUESTS:
X  -----------------------
X
X  The user may request help in general, or on a specific command; he can
X  'set list mail ack' in which case his/her messages to the list will be
X  echoed back to him/her, and 'set list mail noack' (the opposite);
X  A 'set list mail postpone' request will not send any messages to the
X  subscriber until he resets it to one of the other options (used to suppress
X  sending email temporarily). 'set list mail digest' will only send messages
X  periodically as digests. 'set list' with no arguments returns the current
X  values for all options. The user may reset his/her password with the
X  'set list password current-password new-password' request, and use this
X  password to alter the address he/she is subscribed with: 'set list address
X  current-password new-address'; he may hide his identity by issuing a
X  'set list conceal yes' request.
X
X  To subscribe to the list, a user has to give his/her full name; 
X  he/she may leave the list by issuing an 'unsubscribe' request;
X  a list of the current subscribers is obtained through a 'recipients' request;
X  a copy of the sender's message is also sent to peer lists in this case.
X  Moreover, general information is available by means of an 'information'
X  request, and finally 'statistics' compiles a count of messages sent by
X  each subscriber (unless specific subscriber address are given) as well as
X  a total count of messages on file; again, a copy of the message will be
X  forwarded to all peer lists. Private lists do not allow non-members to
X  execute 'recipients' and 'statistics' requests.
X
X  A list of all mailing lists served by this server can be obtained by
X  a 'lists' request.
X
X  A listing of all lists a person is subscribed in is available via a
X  'which' request.
X
X  Information on the current release of this server can be obtained by
X  a 'release' request.
X
X  Subscribers of a list may also run a set of UNIX commands as defined by the
X  system's manager via the 'run' request. Each command may have a different
X  password and may take arguments. Users receive the output from stdout and/or
X  stderr.
X
X  The system includes a means for users to obtain files. The files
X  can be placed anywhere in the system and are archived under
X  			HOMEDIR/archives/*
X  Each of these archives has an index of subarchives and a directory
X  of files that can be obtained from that archive; there is a master
X  archive in archives/listserver: the file INDEX contains names and full paths
X  to all of the archives (including listserver); the file DIR is a directory 
X  of files that are archived under listserver. A user
X  may obtain a list of all the archives and their files by sending an
X  'index' request. That request followed by a specific archive will
X  send a list of files for that archive and all of its subsrchives.
X  The format of the INDEX file is as follows: one line per archive
X  with the following:
X
X	 archive-name full-path-to-its-directory [password]
X
X  When listing subarchives in INDEX the following rule has to be followed:
X  the first entry is the archive itself; all first level subarchives follow
X  (let's say A, B and C), then all second level subarchives (starting with
X  A's subarchives, then listing B's and last C's), then all third level
X  subarchives, etc. No sibling archives may have the same name.
X
X  An archive is made private by putting a password after its full
X  path specification. This password has to be present in all parent
X  archives and is required for obtaining an index of that archive,
X  as well as any files from that archive.
X
X  A file can be obtained via a 'get' request, specifying the archive and
X  the file to get. It is possible that a file has been split in various
X  parts, in which case multiple emails with the various subparts will
X  be sent to the user. Note that only the master index is used in this
X  case for locating the archive. Individual parts of the split file may
X  be obtained by specifying them as arguments.
X
X  An archive's files may be searched for a pattern via the 'search' request.
X  The pattern is an egrep(1)-style regular expression with the additional
X  `&` (AND) and '~' (OR) loginal operators.
X
X  A file may be archived with the farch utility, or manually by
X  editing the DIR file of the target archive. The format is as follows:
X  one line per file containing the following information:
X
X  filename number-of-subparts size-of-each-part dir-where-found [Comments]
X
X  The restriction is that the actual disk file should be all in lower case.
X  Private archives require a password for obtaining files.
X
X  A new archive may be created by creating a subdirectory under
X  HOMEDIR/archives (the new directory may not be all in lower-case letters),
X  updating the master INDEX (archives/listserver/INDEX) and all lower-level
X  indeces (if any), creating a new INDEX file in the new directory with one
X  entry (the new archive itself), and creating an empty DIR file in the
X  same new directory.
X
X  The hierarchical structure is only logical and directory hierarchy does
X  not matter. All archives though have to be placed under HOMEDIR/archives.
X
X  All commands may be abbreviated except 'shutdown' and 'restart'.
X
X  LIST OWNER ORIENTED REQUESTS:
X  -----------------------------
X
X  The system includes support for list owners. These are list administrators
X  with special privileges on the system. They may issue requests on users'
X  behalf overriding any restriction set on regular users (these include
X  disabled commands also). The following requests are available to
X  list owners:
X
X    system <list> <password> <user-address> #<user-request>
X
X  This request overrides all system restrictions and executes <user-request>
X  on behalf of <user-address>; the address has to appear as listsed in the
X  .subscribers file, where applicable. The most frequent use of the 'system'
X  request is to subscribe a user to a private list. ListServer will send
X  a message to the owner igiving him the exact address and request to
X  issue back. For example:
X
X	system herc herc1 user1@foo.com #subscribe herc Foo Bar
X
X  As another example, to remove a user from a list, the owner issues:
X
X	system herc herc1 user1@foo.com #unsubscribe herc
X
X  Keep in mind that if the owner makes a syntax error in <user-request>,
X  <user-address> will be notified as if they issued the incorrect
X  request.
X
X  A list owner may obtain all reports pertinent to the list he is maintaining
X  by issuing the following request:
X
X    reports <list> <password>
X
X  This will send two mail messages: one with the current report and one with
X  the previously archived ones.
X
X  An owner may also obtain other files pertinent to his list via the
X  following request:
X
X    edit <list> <password> <file>
X
X  This will send a message containing the specified file.
X
X  A list owner may also add aliases to the .aliases file, add users to the
X  .ignored file, change the welcoming message in .welcome, change
X  the informative message in .info, change the .subscribers/.peers/.news etc
X  by way of the 'put' request:
X
X    put <list> <password> <keyword> [args]
X
X  The keyword defines the action to be taken; valid keywords are: alias,
X  ignore, welcome, info. To add an alias, the following request may be
X  issued:
X
X	put <list> <password> alias <new-alias> <address-as-subscribed>
X
X  Refer to the man pages for explanation on aliases. To add to the .ignored
X  file:
X
X	put <list> <password> ignore <address-as-subscribed-or-aliased>
X
X  To create new .welcome/.info/.aliases/.ignored/.subscribers/.news/.peers
X  files the following requests may be issued:
X
X	put <list> <password> welcome
X	put <list> <password> info
X	put <list> <password> aliases
X	put <list> <password> ignored
X	put <list> <password> subscribers
X	put <list> <password> news
X	put <list> <password> peers
X
X  After the request, all text that follows is assumed to be the message
X  to be copied until the end of this message. Thus no more requests can
X  be made in the same mail message (they are treated as regular text).
X
X  As a final note, list owners are authenticated by checking their
X  addresses in HOMEDIR/owners (a file containing all owners' addresses),
X  and verifying their passwords. There is no provision for adding restricted
X  users, adding new peers and connecting with news groups. These cases
X  have to be handled by the system's manager.
X
X  A list owner may also approve messages for posting to his moderated
X  list via an 'approve' request. ListServer forwards every new message
X  to the owner providing him with the message's tag number. The
X  owner then replies with:
X
X	approve <list> <password> <tag>
X
X  ListServer then finds the message with the provided tag and prepares
X  it for posting. To discard a message, the owner sends the following
X  request:
X
X	discard <list> <password> <tag>
X
X  MANAGER ORIENTED REQUESTS:
X  --------------------------
X
X  The entire system may be remotely shut down by way of a 'shutdown' request;
X  this request must be followed by a password that must match the one defined
X  in config. Note that this may result in requests being queued with the
X  'shutdown' one not being serviced; note thought that a copy of the original
X  requests can be found in MAIL_COPY as defined in listserv.h.
X  Likewise, the system may be remotely restarted by issuing a 'restart'
X  request with the proper password. Note that a 'restart' request has no
X  effect after a 'shutdown' (because the server is not running), and that
X  any requests queued with the 'restart' will be serviced (the system will
X  not restart until all requests are serviced).
X
X  ListServer lets the manager execute a command remotely via the 'execute'
X  request, and sends him the output from stdout and stderr in two separate
X  messages.
X
X  COMMAND LINE OPTIONS:
X    -1: Same as for the list program.
X    -r: This option may be repeated an infinite number of times and is
X        always followed by a valid ListServer command (as outlined above).
X        This forces a restriction to be placed on the specified command;
X        whenever a user makes such a request, the request will be rejected
X        if the number of users currently on the system is greater than
X        the threshold specified in listserv.h. This option was added due
X        to the fact that the 'statistics' request may take up a lot of
X        resources to complete.
X    -e: Echo reports to the screen.
X    -n: Do not notify peer servers.
X    -d: Disable a ListServer command. This makes totally unknown to the server.
X        However, help is still available for the particular request.
X    -a: Usually, subscriptions are automatic. This option turns automatic
X	subscription off for the specified list. Such requests will be
X	forwarded to manager for approval.
X    -b: The command that follows will be executed in batch mode.
X    -B: Process the batch queue.
X    -i: Go to interactive mode. No mail is sent out and the text saved
X	in MAILFORWARD will be read by serverd.
X    -D: Turn debug on. A transaction of the last email sent out is kept
X        in the files HOMEDIR/sent and HOMEDIR/received. This assumes
X	use of the 'system' mail method.
X
X  EXIT CODES:
X    0: OK
X    1: Could not open or lock file
X    2: SIGINT signal
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X    9: Too many multiple recipients
X   10: Could not deliver mail
X   11: Malloc failed
X   12: Cannot fork
X   13: Socket connection problem
X   14: Semaphore error
X   15: Cannot setuid, setgid
X   16: Internal error
X
X  Approximate algorithm:
X  {
X    Place a lock so that no other programs will access the same files.
X    Lock SERVER_MAIL_FILE or BATCH_FILE so catmail can't append to it
X    Read the SERVER_MAIL_FILE or BATCH_FILE
X    Unlock the file
X
X    if new messages have arrived then {
X      For each message do {
X        If the message is sent by MAILER-DAEMON forward it to MANAGER
X        else {
X	  if the person in not in the IGNORED file, then
X          scan each line of the body of the message and treat it as a
X          command with possible arguments. Reply to the sender for each such
X          request. If a request cannot be processed, send an error message to
X          the sender and ignore all subsequent requests.
X        }
X      }
X    }
X  }
X
X  Required files:
X    SUBSCRIBERS       <-- The list of subscribed people (diff. for each list)
X    ALIASES	      <-- Aliases of email addresses of subscribers, news &
X    			  peers.
X    PEERS	      <-- A list of peers for a particular list (where appl.)
X    IGNORED           <-- The list of undesired people (one for the server
X			  and one for each list)
X    SERVER_LOCK_FILE  <-- Lock file
X
X  Input files:
X    HEADERS           <-- Used for the 'statistics' request (see list.c)
X    SERVER_MAIL_FILE  <-- Where new messages go
X    BATCH_FILE	      <-- Where requests are batched
X    MAIL_COPY         <-- Copy of this file (actual work file)
X    MSG_NO            <-- Read last message count
X    SUBSCRIBERS
X    IGNORED     
X
X  Output files:
X    SERVER_MBOX       <-- A log of all messages sent
X    SUBSCRIBERS       <-- After updating
X    OLD_SUBSCRIBERS   <-- Temporary
X    MSG_NO            <-- Write last message count
X    REPORT_SERVER     <-- Progress report
X    MAILFORWARD       <-- Completed message (with header and the
X                          the body of the message) to be forwarded
X
X  Format of the SUBSCRIBERS, PEERS and IGNORED files:
X    See comments for list.c
X
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#include <ctype.h>
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X  && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <math.h>
X#if !defined (stellar) && !defined (unknown_port)
X# include <time.h>
X#endif
X#include <sys/time.h>
X#if !defined (sequent) && !defined (__NeXT__) && !defined (__convex__) && \
X !defined (apollo) && !defined (i386) && !defined (unknown_port)
X# include <sys/termio.h>
X#endif
X#ifndef sun
X# include <sys/ioctl.h>
X#endif
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include "defs.h"
X#include "listserv.h"
X#include "struct.h"
X#include "global.h"
X#include "iulp.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X#ifdef TCP_IP
X# include <sys/socket.h>
X# include <netdb.h>
X# include <netinet/in.h>
Xstruct	 in_addr localaddr;
X#else
Xchar	 *localaddr;
X#endif
X
X/* 
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X# include "ansi/misc.h"
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern int  siul (char *, int, char *, char *, int, char *, char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include "nonansi/misc.h"
X# include <varargs.h>
Xextern int  syscom ();
Xextern int  siul ();
Xextern char *tsprintf ();
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern void report_progress (FILE *, char *, int);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern void setup_string (char *, char *, char *);
Xextern char *upcase (char *);
Xextern char *locase (char *);
Xextern void distribute (FILE *, void (*)(char *, char *, BOOLEAN),
X			FILE *, char *, char *, char *, char *);
Xextern char *clean_name (char *);
Xextern void clean_request (char *);
Xextern int  _getopt (int, char **, char *);
Xextern void get_list_name (char *, char *);
Xextern int  get_list_id (char *, SYS *, int);
Xextern void shrink (char *);
Xextern void free_remote (REMOTE **);
Xextern void check_aliases (char *, char *);
Xextern void shadow_password (char *);
Xextern void read_params (char *, char *, char *, FILE *, FILE *);
Xextern BOOLEAN extract_subscriber (FILE *, char *, BOOLEAN);
Xextern BOOLEAN extract_sender (char *);
Xextern BOOLEAN sysmail (char *);
Xextern BOOLEAN subscribed (FILE *, char *, char *, char *, char *, char *);
Xextern BOOLEAN strinstr (char *, char *);
Xextern BOOLEAN ignore_sender (FILE *, char *, FILE *, BOOLEAN);
Xextern BOOLEAN requested_part (char *, int);
Xextern BOOLEAN owner_listed (char *, char *, char *, FILE *);
Xextern BOOLEAN remove_msg (char *, int, FILE *);
Xextern int  lock_file (char *, int, int, BOOLEAN);
Xextern void unlock_file (int);
Xextern void escape_address (char *);
Xextern int  otoi (char *);
Xextern int  insert_word (char *, char **, int, int, int);
Xextern char *_strstr (char *, char *);
Xextern int  re_strcmp (char *, char *, char *);
Xextern char *mystrdup (char *);
Xextern int echo (char *, char *);
Xextern int echo_append (char *, char *);
Xextern int mv (char *, char *);
Xextern int cp (char *, char *);
Xextern int cat_append (char *, char *);
Xextern int touch (char *);
Xextern int ucb_strftime (char *, int, char *, struct tm *);
Xextern long int write_to_fd (int, char *, long int);
X
Xvoid   main (int, char **, char **);
Xvoid   process_message (char *, char *, BOOLEAN);
Xvoid   action (char *, char *, char *, BOOLEAN);
Xvoid   reply_code (int);
Xvoid   create_header (FILE **, char *, char *, char *, char *, BOOLEAN, int,
X		      BOOLEAN, BOOLEAN);
Xvoid   reject_mail (char *, char *, char *, int, int);
Xvoid   System (char *, char *, char *);
Xvoid   get_sys_files (char *, char *, char *);
Xvoid   put (char *, char *, char *);
Xvoid   help (char *, char *, char *);
Xvoid   unsubscribe (char *, char *, char *);
Xvoid   subscribe (char *, char *, char *, BOOLEAN);
Xvoid   which (char *, char *, char *);
Xvoid   set (char *, char *, char *);
Xvoid   recipients (char *, char *, char *);
Xvoid   info (char *, char *, char *);
Xvoid   stats (char *, char *, char *);
Xvoid   Shutdown (char *, char *, char *);
Xvoid   restart (char *, char *, char *);
Xvoid   lists (char *, char *, char *);
Xvoid   Index (char *, char *, char *);
Xvoid   get (char *, char *, char *);
Xvoid   search (char *, char *, char *);
Xvoid   release (char *, char *, char *);
Xvoid   notify (char *, char *, char *);
Xvoid   approve (char *, char *, char *);
Xvoid   discard (char *, char *, char *);
Xvoid   notify_peer_servers (char *, char *, char *, char *);
Xvoid   execute (char *, char *, char *);
Xvoid   unix_cmd (char *, char *, char *);
Xvoid   init_commands (void);
Xvoid   usage (void);
Xvoid   server_config (char *);
Xint    gexit (int);
X
X/*
X  The control structure of the server. Check if mail has arrived.
X  If so, copy it to MAIL_COPY and proceed to lower level.
X  First, the command line options are analyzed (for restrictions, etc.).
X*/
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  char *options = "1r:d:enDa:b:c:Bi", *mask;
X  int c;
X  BOOLEAN execute_once = FALSE, notok;
X  int i, j, k, rlfd = 2;
X  FILE *f;
X  extern char *optarg, *getenv();
X  extern int optopt;
X#ifdef TCP_IP
X  struct hostent *lhost;
X#endif
X
X  prog = argv[0];
X  init_commands();
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  if ((report = fopen (REPORT_SERVER, "a")) == NULL)
X    fprintf (stderr, "listserv: Unable to open %s\n", REPORT_SERVER),
X    exit (1);
X  chmod (REPORT_SERVER, 384); /* 600 */
X#endif
X  nlists = sys_config (report, &sys);
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case '1': execute_once = TRUE; break;
X      case 'e': tty_echo = TRUE; break;
X      case 'i': interactive = TRUE;
X      case 'n': do_not_notify_peer_server = TRUE; break;
X      case 'a':
X      case 'c':
X	if ((listid = get_list_id (upcase (optarg), &sys, nlists)) < 0)
X	  fprintf (stderr, "\nlistserv: Unknown list %s for -%c option\n",
X		   optarg, c),
X	  exit (3);
X	if (c == 'a')
X	  sys.lists[listid].options |= NON_AUTO_SUB;
X	else if (c == 'c')
X	  sys.lists[listid].options |= CONCEAL_LIST;
X	break;
X      case 'B': process_batch = TRUE; break;
X      case 'D': debug = TRUE; break;
X      case 'b':
X      case 'r':
X      case 'd':
X        notok = TRUE;
X        k = 0;
X        upcase (optarg);
X        for (i = 0; i < MAX_COMMANDS; ++i) {
X          notok &= (((j = strncmp (optarg, commands[i].name, strlen (optarg)))
X                    != 0) ? 1 : 0);
X          if (!j) {
X            ++k;
X	    if (c == 'r')
X              restricted_commands |= commands[i].mask;
X	    else if (c == 'd')
X	      disabled_commands |= commands[i].mask;
X	    else if (c == 'b')
X	      batched_commands |= commands[i].mask;
X	  }
X	}
X        if (notok)
X          fprintf (stderr, "listserv: Unrecognized request '%s'\n", optarg),
X          exit (3);
X        if (k > 1) /* ambiguous command */
X          fprintf (stderr, "listserv: Ambiguous request '%s'\n", optarg),
X          exit (3);
X        break;
X      case ':':
X          fprintf (stderr, "listserv: Option '%c' requires an argument.\n",
X		   optopt);
X          exit (3);
X      case '?':
X      default:
X        usage ();
X    }
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (SERVER_LOCK_FILE, O_RDWR, 0, FALSE)) < 0)
X    fprintf (stderr, "listserv: Unable to lock %s. Aborting.\n", 
X             SERVER_LOCK_FILE),
X    exit (1);
X#endif
X  if (!execute_once)
X    printf ("%s", COPYRIGHT);
X  init_signals();
X  catch_signals();
X  if (sys.options & USE_ENV_VAR) {
X    if ((sys.mail.method = (char *) malloc (256 * sizeof (char))) == NULL)
X      report_progress (report, "\nmain(): malloc() failed", TRUE),
X      gexit (11);
X    sprintf (sys.mail.method, "env - %s=%s %s ", sys.mail.env_var,
X	     sys.server.address, sys.mail.mail_prog);
X   }
X  if ((msg_no = fopen (MSG_NO, "r")) != NULL)
X    fscanf (msg_no, "%d\n", &request_no),
X    fclose (msg_no);
X#if defined (TIOCNOTTY) && defined (SIGTTOU) && defined (SIGTTIN)
X  if (!tty_echo)
X    if ((i = open ("/dev/tty", 2)) >=0)
X      ioctl (i, TIOCNOTTY, 0),
X      close (i);
X#endif
X    
X  if ((f = fopen (PID_SERVER, "w")) != NULL)
X    fprintf (f, "%d", getpid()),
X    fclose (f);
X  signal (SIGINT, (void (*) ()) gexit);
X  signal (SIGALRM, SIG_IGN);
X#ifdef TCP_IP
X  gethostname (hostname, sizeof (hostname));
X  lhost = gethostbyname (hostname);
X  memcpy ((char *) &localaddr, (char *) lhost->h_addr, lhost->h_length);
X#else
X  strcpy (localaddr, LOCAL_ADDR);
X  strcpy (hostname, HOSTNAME);
X#endif
X
X  if (process_batch) /* Select file to process: batch or requests */
X    strcpy (requests_file, BATCH_FILE);
X  else if (!interactive)
X    strcpy (requests_file, SERVER_MAIL_FILE);
X  else
X    strcpy (requests_file, LIVE_REQUESTS_F);
X
X  do {
X    if (!stat (requests_file, &stat_buf) && stat_buf.st_size > 0) {
X#ifndef NO_LOCKS
X      if ((rlfd = lock_file (requests_file, O_RDWR, 0, FALSE)) < 0)
X	break; /* Cannot lock file, so exit, regardless of execute_once */
X#endif
X      cp (requests_file, MAIL_COPY);
X      if (!process_batch)
X        cat_append (MAIL_COPY, SERVER_MBOX),
X        echo_append ("", SERVER_MBOX);
X      if (!unlink (requests_file)) {
X	touch (requests_file); /* rewrite file */
X	if (process_batch)
X	  chmod (requests_file, /*384*/ 0666 & (0666 ^ otoi (mask)));
X	else
X	  chmod (requests_file, /*416*/ 0666 & (0666 ^ otoi (mask)));
X      }
X#ifndef NO_LOCKS
X      unlock_file (rlfd);
X#endif
X      OPEN_FILE (mail, MAIL_COPY, "r", "main");
X      if (process_batch)
X	report_progress (report, PROCESSING_BATCH, FALSE);
X      else
X        report_progress (report, NEW_ARRIVAL, FALSE);
X      distribute (mail, (void (*)(char *, char *, BOOLEAN)) process_message,
X		  report, NULL, NULL, NULL, NULL);
X      fclose (mail);  /* Done */
X      shrink (message_idsf);
X      unlink (MAIL_COPY);  /* Done delivering */
X    }
X    else if (!execute_once) /* No mail to deliver */
X      if (sys.frequency > 0)
X        sleep (sys.frequency);
X  } while (!execute_once);
X#ifdef SYSLOG
X  closelog ();
X#else
X  fclose (report);
X#endif
X  free_remote (&rlists);
X  if (restart_sys)
X    gexit (7); /* Exit status of 7 signifies a restart request */
X  gexit (0);
X}
X
X/*
X  Process each message. Isolate each command and pass it to action().
X  Before that, check if the message is from MAILER_DAEMON, in which case
X  forward the message to MANAGER. Any messages from people listed in the
X  IGNORED file are not processed.
X  Note: Please look at the documentation for list.c for the definition of a
X  mailer daemon.
X*/
X
Xvoid process_message (char *sender, char *linecopy, BOOLEAN address_ok)
X{
X  char line [MAX_LINE];          /* ... from the current message */
X  char request [MAX_LINE];       /* holds each command */
X  char sender_copy [MAX_LINE];
X  char senders_subject [MAX_LINE];
X  char id_copy [MAX_LINE];
X  char original_sender [MAX_LINE];
X  char received [MAX_LINE];
X  char match [MAX_LINE];
X  char error [MAX_LINE];
X  char *at = NULL;
X  FILE *f;
X  BOOLEAN loop = FALSE;
X  BOOLEAN did_action;
X  BOOLEAN done;
X  BOOLEAN fake_mail = TRUE;
X  BOOLEAN mailer_daemon;
X  BOOLEAN susp_subject;
X
X  listid = -1;
X  peer_server_request = FALSE;
X  line[0] = message_id[0] = id_copy[0] = RESET (sender_copy);
X  strcpy (sender_copy, sender);  /* We do not like converting the actual */
X  upcase (sender_copy);          /* address to upper case */
X  report_progress (report, tsprintf ("Request #%04d:%s\n", ++request_no,
X    (*sender != EOS ? sender : "\"\"")), FALSE);
X  OPEN_FILE (msg_no, MSG_NO, "w", "process_message");
X  fprintf (msg_no, "%d\n", request_no);
X  fclose (msg_no);
X
X  received[0] = RESET (senders_subject);
X  while (!feof (mail) && line[0] != '\n') { /* Skip to beginning of message */
X    strcpy (match, "\\1");
X    if (re_strcmp (SUBJECT, line, match) > 0) {
X      strcpy (senders_subject, line + strlen (match));
X      sprintf (line, "%s", line + strlen (match));
X      strcpy (match, "\\1");
X      if (re_strcmp (PEER_SERVER_REQUEST, line, match) > 0)
X	strcpy (psr_string, match),
X	peer_server_request = TRUE;
X    }
X    else if (re_strcmp (FROM, line, match) > 0)
X      strcpy (original_sender, line + strlen (match)), /* Save From: */
X      original_sender [strlen (original_sender) - 1] = EOS; /* \n -> \0 */
X    else if (re_strcmp (RECEIVED, line, match) > 0) { /* Check for fake mail */
X      strcpy (received, line + strlen (match));
X      received [strlen (received) - 1] = EOS;
X      sscanf (received, "%s %s", error, received); /* Get host after "from" */
X      upcase (received);  /* Relay host */
X      if (fake_mail && (at = strchr (sender_copy, '@')))
X	if (re_strcmp (at + 1, received, NULL) > 0)
X	  fake_mail = FALSE;
X    }
X    else if (re_strcmp (MESSAGE_ID1, line, NULL) > 0 ||
X	     re_strcmp (MESSAGE_ID2, line, NULL) > 0 ||
X	     re_strcmp (MESSAGE_ID3, line, NULL) > 0)
X      strcpy (message_id, line + strlen ("Message-") + 4),
X      message_id [strlen (message_id) - 1] = EOS, /* \n -> \0 */
X      strcpy (id_copy, message_id);
X      upcase (id_copy);
X    RESET (line);
X    fgets (line, MAX_LINE - 2, mail);
X  }
X
X  if (message_id[0] != EOS) { /* Check for mail loop using Message-Id: */
X    SETUP_MESSAGE_IDSF;
X    if (message_ids = fopen (message_idsf, "r")) {
X      if (ignore_sender (message_ids, id_copy, report, TRUE))
X        loop = TRUE;
X      fclose (message_ids);
X    }
X    if (!loop) {
X      OPEN_FILE (message_ids, message_idsf, "a", "process_message");
X      fprintf (message_ids, "%s %s\n", message_id, sender); /* Save new id */
X      fclose (message_ids);
X    }
X  }
X
X  if (!address_ok) {
X    RESET (request);
X    NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS (sender);
X    one_rejection = TRUE;
X  }
X
X  mailer_daemon = strinstr (MAILER_DAEMON, sender_copy) ||
X    (original_sender[0] != EOS && strinstr (MAILER_DAEMON, original_sender)) ||
X    *sender == EOS;
X  susp_subject = (senders_subject[0] != EOS &&
X		  strinstr (SUSP_SUBJECT, senders_subject));
X  if (mailer_daemon || susp_subject) {
X    /* Send message to MANAGER */
X    create_header (&f, MAILFORWARD, sys.server.address, sys.manager,
X		   senders_subject, FALSE, INVALID_REQ, TRUE, TRUE);
X    fprintf (f, "\nRejected message: sent to %s by %s follows.\n\
XReason for rejection: %s.\n--------------------------------------\
X-----------------------------------------\n", sys.server.address,
X	     (*sender != EOS ? sender : "\"\""),
X	     (mailer_daemon ? "suspicious address" :
X	      (susp_subject ? "suspicious subject" : "???")));
X    RESET (line);
X    while (!feof (mail) && 
X           (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {
X      fprintf (f, "%s", line);
X      RESET (line);
X      fgets (line, MAX_LINE - 2, mail);
X    }
X    COMPLETE_TELNET (f);
X    fclose (f);
X    DELIVER_MAIL (sys.manager, FALSE);
X    report_progress (report,
X		     tsprintf ("Forwarding message to %s (%s)\n", sys.manager,
X			       (mailer_daemon ? "suspicious address" :
X				(susp_subject ? "suspicious subject" : "???"))),
X		     FALSE);
X    did_action = TRUE;
X  }
X  else { /* Isolate request and call action() */
X    check_aliases (ALIASESF, sender);
X    done = did_action = FALSE;
X    if (!interactive && fake_mail && at && !one_rejection && !loop)
X      report_progress (report,
X		       tsprintf ("*** Possible fake mail: Host %s in user \
Xaddress does\nnot match any of the domains in the Received: header lines.\n",
X				 at + 1), FALSE);
X    while (!feof (mail) && 
X           (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {
X      clean_request (line);
X      RESET (request);
X      sscanf (line, "%s ", request);
X      upcase (request);
X      if (!strcmp (request, START_OF_SIGNATURE) || strinstr (THANKS, request))
X	done = TRUE; /* End of requests, start of .signature */
X      else if (request[0] != EOS && !one_rejection && !loop && !done)
X	if (!re_strcmp ("OFFICE[ \t]+MEMO|Subject|:.$|^\\>|^\\]|^\\||^#|^%|^\\}",
X	    line, NULL))
X          action (line, request, sender, FALSE),
X	  did_action = TRUE;
X      RESET (line);
X      fgets (line, MAX_LINE - 2, mail);
X    }
X  }
X  if (!did_action && !one_rejection && !loop) {
X    create_header (&f, MAILFORWARD, sys.server.address, sender,
X		   "No requests found", FALSE, INVALID_REQ, FALSE, FALSE);
X    fprintf (f, "No requests found in your message. Requests should be \
Xincluded in the\nbody of the mail message.\n");
X    COMPLETE_TELNET (f);
X    fclose (f);
X    DELIVER_MAIL (sender, FALSE);
X  }
X  strcpy (linecopy, line);
X  one_rejection = FALSE;
X  report_progress (report, "", -TRUE); /* -TRUE for no leading newline */
X}
X
X/*
X  Recognize the 'request' and call the appropriate routine, or reject the
X  message if the request is not recognized. Isolate any parameters.
X  If a restriction is in force for the recognized request, then get
X  the number of users currently on the system and reject the request if
X  this number is greater that the predetermined threshold.
X  A request may be batched if we are not processing the batch queue and
X  the specified request is indeed to be batched according to the command
X  line options.
X*/
X
Xvoid action (char *line, char *request, char *sender, BOOLEAN override)
X{
X  char params [MAX_LINE];
X  char error [MAX_LINE];
X  char list_name [MAX_LINE];
X  char *users_file;
X  int i, nusers;
X  FILE *f, *ignored;
X  long int time_is = 0;
X  struct tm *t;
X
X  report_progress (report, line, FALSE);
X  listid = -1;
X  RESET (error);
X  SETUP_IGNOREDF;
X  if ((ignored = fopen (server_ignoredf, "r"))) { /* No file for remote list */
X    if (ignore_sender (ignored, sender, report, FALSE))
X      goto abort;
X    fclose (ignored);
X  }
X  upcase (sender);
X  original_params[0] = RESET (params);
X  read_params (line, params, request, mail, report);
X  strcpy (original_params, params); /* Preserve case */
X  upcase (params);
X  for (i = 0; i < MAX_COMMANDS; i++) /* Walk through the valid requests */
X    if (! strncmp (request, commands[i].name, strlen (request)) &&
X	strlen (request) > 2 && ! (disabled_commands & commands[i].mask)) {
X      if (strncmp (request, "SHUTDOWN", strlen (request)) &&
X          strncmp (request, "RESTART", strlen (request)) &&
X          strncmp (request, "LISTS", strlen (request)) &&
X	  strncmp (request, "INDEX", strlen (request)) &&
X	  strncmp (request, "GET", strlen (request)) &&
X	  strncmp (request, "FAX", strlen (request)) &&
X	  strncmp (request, "SEARCH", strlen (request)) &&
X	  strncmp (request, "RELEASE", strlen (request)) &&
X	  strncmp (request, "WHICH", strlen (request)) &&
X	  strncmp (request, "NOTIFY", strlen (request)) &&
X	  strncmp (request, "EXECUTE", strlen (request)) &&
X          strncmp (request, "HELP", strlen (request))) {
X        get_list_name (params, list_name);
X        listid = get_list_id (list_name, &sys, nlists);
X        server_config (list_name);
X        if (listid < 0) {
X          if (list_name[0] == EOS)
X            sprintf (error, "%s: Missing list name\n\n\
XSyntax: %s <list> [...]\n", request, request);
X          else
X            sprintf (error, "%s: Unknown list name %s\n", request, list_name);
X          reject_mail (sender, line, error, SYNTAX_ERROR, REJECT_LIST);
X          return;
X        }
X        if ((ignored = fopen (ignoredf, "r"))) { /* No file for remote list */
X          if (ignore_sender (ignored, sender, report, FALSE)) {
X	    fclose (ignored);
X            goto abort;
X	  }
X	  fclose (ignored);
X	}
X        if (commands[i].mask & sys.lists[listid].disabled_commands &&
X	    !override) {
X	  reject_mail (sender, line,
X		       tsprintf ("%s requests for list %s are disabled\n",
X				 request, sys.lists[listid].alias),
X		       INVALID_REQ, 0);
X	  goto abort;
X	}
X      }
X      if (restricted_commands & commands[i].mask && !override) {
X	/* Restriction set */
X#ifdef _AIX
X        syscom ("%s | %s -F, '{ print $2 }' > %s", UPTIME, AWK, 
X		(users_file = mystrdup (tmpnam (NULL))));
X#else
X        syscom ("%s | %s -F, '{ print $3 }' > %s", UPTIME, AWK,
X		(users_file = mystrdup (tmpnam (NULL))));
X#endif
X	OPEN_FILE (f, users_file, "r", "action");
X        fscanf (f, "%d", &nusers);
X        fclose (f);
X        unlink (users_file);
X	free ((char *) users_file);
X        if (nusers > sys.users) {
X          create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X			 FALSE, RESTRICTED_REQ, FALSE, FALSE);
X          fprintf (f, "This request takes a considerable amount of resources \
Xand certain restrictions\nare currently in force. Please resubmit your \
Xrequest at a later time.\n");
X	  COMPLETE_TELNET (f);
X          fclose (f);
X	  DELIVER_MAIL (sender, FALSE);
X          strcat (request, ": restriction enforced\n");
X          report_progress (report, request, FALSE);
X          goto abort;
X        }
X      }
X      if (!process_batch && 
X	  (batched_commands & commands[i].mask)) { /* Batch request */
X	time (&time_is);
X	t = localtime (&time_is);
X	if (t->tm_hour >= sys.batch.start &&
X	    t->tm_hour <= (sys.batch.stop - 1)) { /* Between 8am and 8pm */
X	  if (interactive) { /* Cannot batch a "live" request */
X	    OPEN_FILE (f, MAILFORWARD, "w", "action");
X	    fprintf (f, ">%s\nThe request cannot be processed at this time: \
Xbatch mode in effect.\nPlease send email.\n", line);
X	    fclose (f);
X	    goto abort;
X	  }
X	  OPEN_FILE (f, BATCH_FILE, "a", "action");
X	  time_is = time (0);
X	  fprintf (f, "From %s %s\n\n%s\n", sender, ctime (&time_is), line);
X	  fclose (f);
X	  report_progress (report, "(request placed in the batch queue)\n",
X			   FALSE);
X	  goto abort;
X	}
X      }
X      commands[i].func (request, params, sender, override); /* Call routine */
X      goto abort;
X    }
X  if (! (sys.options & IGNR_INVLD_RQSTS) || interactive)
X    reject_mail (sender, line, tsprintf ("Unrecognized request %s\n", request),
X		 INVALID_REQ, REJECT_REQUEST);
X  else
X    report_progress (report, "[ignored]\n", FALSE);
X  abort: ;
X}
X
X/*
X  Produce the reply code during a live session.
X*/
X
Xvoid reply_code (int reply)
X{
X  FILE *f;
X
X  OPEN_FILE (f, ULISTSERVER_REPLY, "w", "reply_code");
X  fprintf (f, "%d\n", reply);
X  fclose (f);
X}
X
X/*
X  Create a message header addressed to 'sender' with the given 'subject'.
X  If this a reply to an invalid request, 'copy_owner' should be TRUE.
X*/
X
Xvoid create_header (FILE **f, char *filename, char *sender, char *recipient, 
X		    char *subject, BOOLEAN copy_owner, int reply,
X		    BOOLEAN preserve_msg_id, BOOLEAN error_condition)
X{
X#ifdef NEED_DATE
X  char date [80];
X  long int time_is;
X  struct tm *t;
X#endif
X
X  reply_code (reply);
X  OPEN_FILE (*f, filename, "w", "create_header");
X  locase (recipient);
X  if (subject [0] != EOS && subject [strlen (subject) - 1] == '\n')
X    subject [strlen (subject) - 1] = EOS;
X  if (interactive)
X    return;
X  if (!fax_it && sys.options & USE_TELNET) {
X    fprintf (*f, "HELO %s\nMAIL From: <%s>\nRCPT To: <%s>\n",
X#ifdef ZMAILER
X/*
X# ifdef TCP_IP
X	     (char *) inet_ntoa (localaddr),
X# else
X	     localaddr,
X# endif
X	Use either the local address or the host name.
X*/
X	     hostname,
X#else
X	     "",
X#endif
X	     sender, recipient);
X    if (copy_owner)
X      fprintf (*f, "RCPT To: <%s>\n",
X	       (listid >= 0 ? sys.lists[listid].owner : sys.manager));
X    fprintf (*f, "DATA\n");
X  }
X  if (preserve_msg_id && message_id[0] != EOS)
X    fprintf (*f, "Message-Id: %s\n", message_id);
X#ifdef NEED_DATE
X  time (&time_is);
X  t = localtime (&time_is);
X  ucb_strftime (date, 1024, "%a, %e %b %Y %T %Z", t);
X  fprintf (*f, "Date: %s\n", date);
X#endif
X#ifndef NO_ERRORS_TO
X  fprintf (*f, "Errors-To: %s\n",((listid < 0 || listid == nlists) ?
X				  sys.manager : sys.lists[listid].owner));
X#endif
X  fprintf (*f, "Reply-To: %s\nSender: %s\nFrom: %s\nTo: %s\n",
X	   sender, sender, sender, recipient);
X  if (copy_owner && !fax_it)
X    fprintf (*f, "Cc: %s\n",
X	     ((listid >= 0 && listid < nlists) ?
X	      sys.lists[listid].owner : sys.manager));
X  fprintf (*f, "Subject: %s%s\nX-Listserver-Version: %s\n", 
X	   (error_condition ? ERROR_CONDITION : ""), subject, VERSION);
X  if (sys.server.comment[0] != EOS)
X    fprintf (*f, "X-Comment: %s\n\n", sys.server.comment);
X  else
X    fprintf (*f, "\n");
X}
X
X/*
X  Send a message to 'sender' indicating an invalid request. The body of
X  the message is given in 'text'. Only one such mail is sent to the user
X  for each of his/her invalid requests. All subsequent requests are ignored.
X*/
X
Xvoid reject_mail (char *sender, char *request, char *text, int reply, int why)
X{
X  FILE *f;
X  char *newline;
X 
X  if (one_rejection)
X    return;
X  one_rejection = TRUE;
X  create_header (&f, MAILFORWARD, sys.server.address, sender, 
X		 "Invalid request", COPY_OWNER (ccerrors), reply, FALSE, TRUE);
X  fprintf (f, ">%s\n%s\n", request, text);
X  if ((newline = strchr (text, '\n')))
X    *(newline + 1) = EOS;
X  report_progress (report, text, FALSE);
X  if (interactive) {
X    fclose (f);
X    return;
X  }
X  fprintf (f, "Report any problems to '%s'.\nFor a list of the \
Xavailable requests send a message to %s\nwith a body consisting of nothing \
Xbut the word HELP\n\nPS: Any subsequent requests that you might have \
Xsubmitted have been ignored.\n",
X	   ((listid < 0 || listid == nlists) ?
X	    sys.manager : sys.lists[listid].owner),
X	   sys.server.address);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (ccerrors));
X}
X
X/*
X  Execute a system request. This is usually a request issued by the
X  list's owner. System requests are valid only for the list the password
X  is given for.
X*/
X
Xvoid System (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char newrequest [MAX_LINE];
X  char newsender [MAX_LINE];
X  char list [MAX_LINE];
X  char *comment, *c;
X  int target_listid;
X  FILE *f;
X
X  newsender[0] = newrequest[0] = list[0] = RESET (password);
X  sscanf (params, "%s ", password);
X  if ((c = _strstr (params, password))) {
X    while (*c != EOS && !isspace (*c))  /* Get to the end of password */
X      ++c;
X    while (*c != EOS && isspace (*c))   /* Get to new sender address */
X      ++c;
X    sprintf (newsender, "From %s", c);
X    if (!extract_sender (newsender)) {  /* Error in address scanning */
X      shadow_password (params);
X      sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X	       params);
X      NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS (newsender);
X      return;
X    }
X  }
X  shadow_password (params);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params);
X  if (!owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X    NOT_LIST_OWNER; /* Hacker attack */
X    return;
X  }
X  if (password[0] == EOS) {
X    reject_mail (sender, request, "Missing password for SYSTEM request\n\n\
XSyntax: system <list> <password> <address> #<user-request>\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (password[0] == '#' || strcmp (password, sys.lists[listid].password)) {
X    reject_mail (sender, request, "Invalid password for SYSTEM request\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (newsender[0] == EOS || newsender[0] == '#') {
X    reject_mail (sender, request, "Missing user address for SYSTEM request\n\n\
XSyntax: system <list> <password> <address> #<user-request>\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (!(comment = strchr (params, '#'))) { /* No actual request */
X    reject_mail (sender, request, "Missing '#': no system request specified\n\n\
XSyntax: system <list> <password> <address> #<user-request>\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  sprintf (params, "%s", comment + 1); /* Remove password + sender address */
X  clean_request (params); /* Now we have the actual request */
X  sscanf (params, "%s %s", newrequest, list);
X  if (strlen (newrequest) < 3) {
X    reject_mail (sender, request, tsprintf ("Ambiguous request %s\n",
X					    newrequest), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strncmp (newrequest, "SHUTDOWN", strlen (request)) &&
X      strncmp (newrequest, "RESTART", strlen (request)) &&
X      strncmp (newrequest, "LISTS", strlen (request)) &&
X      strncmp (newrequest, "SEARCH", strlen (request)) &&
X      strncmp (newrequest, "INDEX", strlen (request)) &&
X      strncmp (newrequest, "GET", strlen (request)) &&
X      strncmp (newrequest, "FAX", strlen (request)) &&
X      strncmp (newrequest, "RELEASE", strlen (request)) &&
X      strncmp (newrequest, "WHICH", strlen (request)) &&
X      strncmp (newrequest, "NOTIFY", strlen (request)) &&
X      strncmp (newrequest, "EXECUTE", strlen (request)) &&
X      strncmp (newrequest, "HELP", strlen (request)) &&
X      list[0] != EOS) { /* Check target list validity */
X    if ((target_listid = get_list_id (list, &sys, nlists)) < 0) {
X      reject_mail (sender, request, tsprintf ("Unknown list %s\n", list),
X		   SYNTAX_ERROR, 0);
X      return;
X    }
X    if (listid != target_listid) { /* Owner is cheating */
X      reject_mail (sender, request,
X		   tsprintf ("Invalid request for list %s; SYSTEM requests \
Xshould be made only\nfor list %s.\n",
X			     list, sys.lists[listid].alias), INVALID_REQ, 0);
X      return;
X    }
X  }
X  action (params, newrequest, newsender, TRUE);
X}
X
X/*
X  Provide the list owner with the requested files. The accumulated
X  report file is then shrunk. When a file is edited, the last modification
X  time is saved to prevent a subsequent put in case the file got modified
X  in the meantime.
X*/
X
Xvoid get_sys_files (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char req [MAX_LINE];
X  char file [MAX_LINE];
X  char extra [MAX_LINE];
X  FILE *f;
X  struct stat stat_buf;
X
X  req[0] = extra[0] = file[0] = RESET (password);
X  strcpy (req, request);
X  sscanf (params, "%s %s %s", password, file, extra);
X  shadow_password (params);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X	   params);
X  if (!owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X    NOT_LIST_OWNER; /* Hacker attack */
X    return;
X  }
X  if (password[0] == EOS) {
X    reject_mail (sender, request,
X		 tsprintf ("Missing password for %s request\n\n%s\n", req,
X			   (!strncmp (req, "EDIT", strlen (req)) ?
X			    "Syntax: edit <list> <password> <file>\n\
X\tfile: subscribers/aliases/ignored/\
Xnews/peers/info/welcome" : "Syntax: reports <list> <password>")),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.lists[listid].password)) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid password for %s request\n", req),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (!strncmp (req, "REPORTS", strlen (req))) { /* Send reports */
X    if (file[0] != EOS) {
X      reject_mail (sender, request,
X		   tsprintf ("Too many arguments to REPORTS ... : %s\n\n\
XSyntax: reports <list> <password>\n", file), SYNTAX_ERROR, 0);
X      return;
X    }
X    create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		   OK, FALSE, TRUE);
X#ifndef SYSLOG
X    fprintf (f, "*** Here is the latest report file:\n\n");
X#else
X    fprintf (f, "Sorry, this system is using syslog(3) and no reports are \
Xavailable.\n");
X#endif
X    fclose (f);
X#ifndef SYSLOG
X    setup_string (report_listf, sys.lists[listid].alias, REPORT_LIST);
X    cat_append (report_listf, MAILFORWARD);
X#endif
X    APPEND_TELNET ("get_sys_files");
X    DELIVER_MAIL (sender, FALSE);
X#ifndef SYSLOG
X    if (!interactive)
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		     FALSE, OK, FALSE, FALSE);
X    else
X      OPEN_FILE (f, MAILFORWARD, "a", "get_sys_files");
X    fprintf (f, "*** Here is the accumulated report file; this file was shrunk \
Xafter it was sent\nto you, so you may wish to save it:\n\n");
X    fclose (f);
X    setup_string (report_listf, sys.lists[listid].alias, REPORT_LIST_ACC);
X    cat_append (report_listf, MAILFORWARD);
X    APPEND_TELNET ("get_sys_files");
X    DELIVER_MAIL (sender, FALSE);
X    shrink (report_listf);
X#endif
X  }
X  else { /* Request is EDIT */
X    if (extra[0] != EOS) {
X      reject_mail (sender, request,
X		   tsprintf ("Too many arguments to EDIT ... %s: %s\n\n\
XSyntax: edit <list> <password> <file>\n\
X\tfile: subscribers/aliases/ignored/\
Xnews/peers/info/welcome\n", file, extra), SYNTAX_ERROR, 0);
X      return;
X    }
X    create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		   OK, FALSE, FALSE);
X    if (!strcmp (file, "ALIASES")) { /* Send .aliases */
X      fprintf (f, "*** Here is the aliases file:\n\n");
X      fclose (f);
X      cat_append (aliasesf, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (aliasesf, aliases_timestampf);
X    }
X    else if (!strcmp (file, "IGNORED")) { /* Send .ignored */
X      fprintf (f, "*** Here is the ignored file:\n\n");
X      fclose (f);
X      cat_append (ignoredf, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (ignoredf, ignored_timestampf);
X    }
X    else if (!strcmp (file, "INFO")) { /* Send .info */
X      fprintf (f, "*** Here is the info file:\n\n");
X      fclose (f);
X      cat_append (infof, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (infof, info_timestampf);
X    }
X    else if (!strcmp (file, "SUBSCRIBERS")) { /* Send .subscribers */
X      fprintf (f, "*** Here is the subscribers file:\n\n");
X      fclose (f);
X      cat_append (subscribersf, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (subscribersf, subscribers_timestampf);
X    }
X    else if (!strcmp (file, "WELCOME")) { /* Send .welcome */
X      fprintf (f, "*** Here is the welcome file:\n\n");
X      fclose (f);
X      cat_append (welcomef, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (welcomef, welcome_timestampf);
X    }
X    else if (!strcmp (file, "NEWS")) { /* Send .news */
X      fprintf (f, "*** Here is the news file:\n\n");
X      fclose (f);
X      cat_append (newsf, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (newsf, news_timestampf);
X    }
X    else if (!strcmp (file, "PEERS")) { /* Get .peers */
X      fprintf (f, "*** Here is the peers file:\n\n");
X      fclose (f);
X      cat_append (peersf, MAILFORWARD);
X      APPEND_TELNET ("get_sys_files");
X      DELIVER_MAIL (sender, FALSE);
X      PUT_TIMESTAMP (peersf, peers_timestampf);
X    }
X    else {
X      fprintf (f, "%s: No such file.\nFiles that can be obtained: \
Xsubscribers/aliases/ignored/news/peers/info/welcome\n", file);
X      COMPLETE_TELNET (f);
X      fclose (f);
X      DELIVER_MAIL (sender, FALSE);
X    }
X  }
X}
X
X/*
X  Perform various list owner requests: put user addresses in files, or
X  put new files in the list's directory. The owner may put new aliases
X  in the .aliases file, new users to be ignored in the .ignored file,
X  change the welcome message in .welcome, and change the informative
X  message in .info.
X*/
X
Xvoid put (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char keyword [MAX_LINE];
X  char arg1 [MAX_LINE];
X  char arg2 [MAX_LINE];
X  char arg3 [MAX_LINE];
X  char line [MAX_LINE];
X  char sign [MAX_LINE];
X  char params_copy [MAX_LINE];
X  BOOLEAN signature;
X  FILE *f;
X  struct stat stat_buf;
X  time_t timestamp;
X  long int sig_mask;
X
X  arg1[0] = arg2[0] = arg3[0] = keyword[0] = params_copy[0] = RESET (password);
X  sscanf (original_params, "%s %s %s %s %s %s", password, password, keyword,
X	  arg1, arg2, arg3);	/* Actually only interested in arg[1-3] */
X  sscanf (params, "%s %s", password, keyword);
X  strcpy (params_copy, params);
X  shadow_password (params);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X	   params);
X  if (!owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X    NOT_LIST_OWNER; /* Hacker attack */
X    return;
X  }
X  if (password[0] == EOS) {
X    reject_mail (sender, request, "Missing password for PUT request\n\n\
XSyntax: put <list> <password> <keyword> [args]\n\
X\tkeyword:alias/ignore/subscribers/aliases/news/peers/ignored/info/welcome\n\
X\targs:email address for alias/ignore\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.lists[listid].password)) {
X    reject_mail (sender, request, "Invalid password for PUT request\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (!strcmp (keyword, "ALIAS")) { /* Add to list's, system's aliases files */
X    if (arg1[0] == EOS || arg2[0] == EOS || arg3[0] != EOS) {
X      reject_mail (sender, request, "Wrong number of arguments to \
XPUT ... ALIAS\n\n\
XSyntax: put <list> <password> alias <alias-address> <address-as-subscribed | \
Xregex>\n", SYNTAX_ERROR, 0);
X      return;
X    }
X    echo_append (tsprintf ("%s %s", arg1, arg2), aliasesf);
X    echo_append (tsprintf ("%s %s", arg1, arg2), ALIASESF);
X  }
X  else if (!strcmp (keyword, "IGNORE")) { /* Add to list's ignore file */
X    if (arg1[0] == EOS || arg2[0] != EOS) {
X      reject_mail (sender, request, "Wrong number of arguments to \
XPUT ... IGNORE\n\n\
XSyntax: put <list> <password> ignore <address>\n", SYNTAX_ERROR, 0);
X      return;
X    }
X    echo_append (tsprintf ("%s", arg1), ignoredf);
X  }
X  else if (!strcmp (keyword, "WELCOME") || !strcmp (keyword, "INFO") ||
X	   !strcmp (keyword, "ALIASES") || !strcmp (keyword, "IGNORED") ||
X	   !strcmp (keyword, "SUBSCRIBERS") || !strcmp (keyword, "NEWS") ||
X	   !strcmp (keyword, "PEERS")) {
X    if (arg1[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X      reject_mail (sender, request,
X		   tsprintf ("Too many arguments to PUT ... %s: %s\n\n\
XSyntax: put <list> <password> <file>\n\
X\tfile: subscribers/aliases/news/peers/ignored/info/welcome\n", keyword, arg1),
X		   SYNTAX_ERROR, 0);
X      return;
X    }
X    if (!strcmp (keyword, "WELCOME")) { /* Write to .welcome */
X      CHECK_TIMESTAMP (welcome_timestampf, welcomef);
X      OPEN_FILE (f, welcomef, "w", "put");
X      unlink (welcome_timestampf);
X    }
X    else if (!strcmp (keyword, "INFO")) { /* Write to .info */
X      CHECK_TIMESTAMP (info_timestampf, infof);
X      OPEN_FILE (f, infof, "w", "put");
X      unlink (info_timestampf);
X    }
X    else if (!strcmp (keyword, "ALIASES")) { /* Write to .aliases */
X      CHECK_TIMESTAMP (aliases_timestampf, aliasesf);
X      OPEN_FILE (f, aliasesf, "w", "put");
X      unlink (aliases_timestampf);
X    }
X    else if (!strcmp (keyword, "IGNORED")) { /* Wite to .ignored */
X      CHECK_TIMESTAMP (ignored_timestampf, ignoredf);
X      OPEN_FILE (f, ignoredf, "w", "put");
X      unlink (ignored_timestampf);
X    }
X    else if (!strcmp (keyword, "SUBSCRIBERS")) { /* Write to .subscribers */
X      CHECK_TIMESTAMP (subscribers_timestampf, subscribersf);
X      OPEN_FILE (f, subscribersf, "w", "put");
X      unlink (subscribers_timestampf);
X    }
X    else if (!strcmp (keyword, "NEWS")) { /* Write to .news */
X      CHECK_TIMESTAMP (news_timestampf, newsf);
X      OPEN_FILE (f, newsf, "w", "put");
X      unlink (news_timestampf);
X    }
X    else { /* Write to .peers */
X      CHECK_TIMESTAMP (peers_timestampf, peersf);
X      OPEN_FILE (f, peersf, "w", "put");
X      unlink (peers_timestampf);
X    }
X#ifdef bsd
X    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X    sighold (SIGINT);
X    sighold (SIGTERM);
X#endif
X    COPY_MESSAGE;
X    fclose (f);
X#ifdef bsd
X    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X    sigrelse (SIGINT);
X    sigrelse (SIGTERM);
X#endif
X    fclose (f);
X  }
X  else {
X    reject_mail (sender, request, "Invalid or missing keyword for PUT \
Xrequest\n\n\
XSyntax: put <list> <password> <keyword> [args]\n\
X\tkeyword:alias/ignore/subscribers/aliases/news/peers/ignored/info/welcome\n\
X\targs:email address for alias/ignore\n", SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  fprintf (f, "Your request was successfully completed.\n");
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, FALSE);
X}
X
X/*
X  Provide help on the specified topic to 'sender'.
X*/
X
Xvoid help (char *request, char *params, char *sender)
X{
X  FILE *f, *index;
X  char param [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char topic [MAX_LINE];
X  char file [MAX_LINE];
X  char shell [MAX_LINE];
X  BOOLEAN found;
X  struct stat stat_buf;
X  int ntopics;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  param[0] = RESET (moreparams);
X  sscanf (params, "%s %s\n", param, moreparams);
X  if (moreparams[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Too many HELP topics: %s\n\n\
XSyntax: help [topic | request]\n", moreparams),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (cchelp), OK, FALSE, FALSE);
X  if (param[0] == EOS)
X    strcpy (param, "GENERAL");
X  found = FALSE;
X  if ((index = fopen (HELP_TOPICS, "r")) == NULL)
X    fprintf (f, "Sorry, no help index found.\n");
X  else {  /* Check index for help topic, get filename */
X    while (!feof (index)) {
X      topic[0] = RESET (file);
X      fscanf (index, "%s %s\n", topic, file);
X      upcase (topic);
X      if (topic[0] != EOS)
X	if (!strncmp (param, topic, strlen (param))) { /* A match */
X	  found = TRUE;
X	  break;
X	}
X    }
X    if (!found) {
X      fprintf (f, "Sorry, no help on topic '%s' currently available.\n\n\
XHelp is available on the following topics:\n\n", param);
X      rewind (index);
X      ntopics = 0;
X      while (!feof (index)) {
X	topic[0] = RESET (file);
X	fscanf (index, "%s %s\n", topic, file);
X	fprintf (f, "%s ", topic);
X	if ((++ntopics) > 9)
X	  ntopics = 0,
X	  fprintf (f, "\n");
X      }
X      fprintf (f, "\n");
X    }
X    else if (stat (file, &stat_buf))
X      fprintf (f, "Sorry, unable to stat file %s for topic '%s'.\n", file,
X	       param),
X      found = FALSE;
X    fclose (index);
X  }
X  fclose (f);
X  if (found) {
X    strcpy (shell, "cat");
X    if ((f = fopen (file, "r"))) {
X      fgets (shell, 3, f);
X      if (!strncmp (shell, "#!", 2)) {
X	fgets (shell, MAX_LINE - 2, f);
X	if (shell [strlen (shell) - 1] == '\n')
X	  shell [strlen (shell) - 1] = EOS;
X      }
X      else
X	strcpy (shell, "cat");
X      fclose (f);
X    }
X    syscom ("%s %s >> %s", shell, file, MAILFORWARD);
X  }
X  APPEND_TELNET ("help");
X  DELIVER_MAIL (sender, COPY_OWNER (cchelp));
X}
X
X/*
X  Unsubscribe a member if he/she is listed in SUBSCRIBERS. If the list
X  is remote, the request is forwarded.
X*/
X
Xvoid unsubscribe (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char error [10240];
X  char param [MAX_LINE];
X  char address [MAX_LINE];
X  BOOLEAN status, ok_to_reset_address = FALSE;
X  long int sig_mask;
X  int i;
X  
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  error[0] = RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid UNSUBSCRIBE option%s\n\
XSyntax: unsubscribe <list> (or signoff <list>)\n", params),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (!(status = subscribed (report, sender, subscribersf, newsf, peersf,
X			     aliasesf))) {
X    if ((sys.lists[listid].defaults.set_values[0][0] == EOS &&
X	 !strcmp (default_values [0], "VARIABLE")) ||
X	(!strcmp (sys.lists[listid].defaults.set_values[0], "VARIABLE")))
X      ok_to_reset_address = TRUE;
X    sprintf (error, "%s: You are not subscribed to %s\n", sender,
X             sys.lists[listid].address);
X    if (alternate_addresses) {
X      sprintf (error + strlen (error),
X	       "\nIn addition, the system found the following \
Xaddress(es) that resemble yours.\nIf one of these is you, please resend your \
Xmessage from that one%s:\n\n",
X	       (ok_to_reset_address ?
X		", or use the\n'set <list> address' request to change the \
Xaddress you are subscribed with" :
X		""));
X      for (i = 0; alternate_addresses[i]; ++i)
X	sprintf (error + strlen (error), "%s\n", alternate_addresses[i]),
X	free ((char *) alternate_addresses[i]);
X      free ((char **) alternate_addresses);
X      alternate_addresses = NULL;
X      strcat (error, "\n");
X    }
X    reject_mail (sender, request, error, INVALID_REQ, 0);
X    return;
X  }
X  else if (status > SUBSCRIBED) { /* Notify manager */
X    NOTIFY_MANAGER ("Attempt to unsubscribe news or peer");
X    return;
X  }
X  /* Now move the current list of subscribers to a temporary file; then
X     copy each entry of this file to SUBSCRIBERS excluding the
X     user to be removed. */
X  sprintf (address, "^[\t ]*%s ", sender);
X  upcase (address);
X  if (cp (subscribersf, OLD_SUBSCRIBERS))
X    exit (16);
X  REMOVE_ADDRESS (address, OLD_SUBSCRIBERS, NEW_SUBSCRIBERS);
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (mv (NEW_SUBSCRIBERS, subscribersf))
X    exit (16);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  if (cp (aliasesf, OLD_ALIASES))
X    exit (16);
X  REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (mv (NEW_ALIASES, aliasesf))
X    exit (16);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  if (cp (ALIASESF, OLD_ALIASES))
X    exit (16);
X  REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (mv (NEW_ALIASES, ALIASESF))
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  sprintf (address, " %s[\t ]*$", sender);
X  upcase (address);
X  if (cp (aliasesf, OLD_ALIASES))
X    exit (16);
X  REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (mv (NEW_ALIASES, aliasesf))
X    exit (16);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  if (cp (ALIASESF, OLD_ALIASES))
X    exit (16);
X  REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (mv (NEW_ALIASES, ALIASESF))
X    exit (16);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  unlink (OLD_SUBSCRIBERS);
X  unlink (OLD_ALIASES);
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccunsub), OK, FALSE, FALSE);
X  fprintf (f, "You have been removed from list %s.\n\
XThanks for being with us.\n", sys.lists[listid].address);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (ccunsub));
X}
X
X/*
X  Subscribe a new user if he/she is not already subscribed. If the list
X  is remote, the request is forwarded. If the list is private, a message
X  is sent to the sender, and another one to the list owner requesting his
X  approval.
X*/
X
Xvoid subscribe (char *request, char *params, char *sender, BOOLEAN override)
X{
X  FILE *f;
X  char name [MAX_LINE];
X  char password [MAX_LINE];
X  char shell [MAX_LINE];
X  long int sig_mask;
X  int i;
X  BOOLEAN status;
X  BOOLEAN live;
X  BOOLEAN chaddr;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  RESET (name);
X  clean_name (params); /* Remove extraneous characters */
X  sscanf (params, "%s\n", name);
X  if (name[0] == EOS) {  /* No user's name */
X    reject_mail (sender, request, "No name given to SUBSCRIBE\n\n\
XSyntax: subscribe <list> <Your Name>\n", SYNTAX_ERROR,
X		 REJECT_NAME);
X    return;
X  }
X  if ((status = subscribed (report, sender, subscribersf, newsf, peersf,
X			    aliasesf)) == SUBSCRIBED) {
X    reject_mail (sender, request,
X		 tsprintf ("%s: You are already subscribed to %s\n", sender, 
X			   sys.lists[listid].address), INVALID_REQ, 0);
X    return;
X  }
X  else if (status > SUBSCRIBED) { /* Notify manager */
X    NOTIFY_MANAGER ("Attempt to subscribe news or peer");
X    return;
X  }
X  if ((sys.lists[listid].options & NON_AUTO_SUB) &&
X      !owner_listed (OWNERSF, sender, sys.lists[listid].alias, report) &&
X      !override) { /* Notify sender/owner */
X    create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		   RESTRICTED_REQ, FALSE, FALSE);
X    fprintf (f, "Subscription requests are not automatic for this list. \
XYour request has\nbeen forwarded to %s for approval.\n",
X	     sys.lists[listid].owner);
X    COMPLETE_TELNET (f);
X    fclose (f);
X    DELIVER_MAIL (sender, FALSE);
X    create_header (&f, MAILFORWARD, sys.server.address, sys.lists[listid].owner,
X		   "Subscription approval request", FALSE, OK, FALSE, FALSE);
X    fprintf (f, "User %s has requested subscription to list %s\n\
XIf you approve, send the following request to %s:\n\n\
Xsystem %s <password> %s #%s\n\n\
Xwhere 'password' is the list's password, as given to you by the manager of\n\
Xthis system.\n",
X	     sender, sys.lists[listid].alias, sys.server.address,
X	     sys.lists[listid].alias, sender, request);
X    COMPLETE_TELNET (f);
X    fclose (f);
X    DELIVER_MAIL (sys.lists[listid].owner, FALSE);
X    return;
X  }
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  OPEN_FILE (f, subscribersf, "a", "subscribe"); /* Automatic subscription */
X  fprintf (f, "%s ", sender);
X  for (i = 1; i < MAX_SET_OPTIONS; i++)  /* Copy all options */
X    if (sys.lists[listid].defaults.set_values[i][0] != EOS) {
X      if (i == 2)
X	strcpy (password, sys.lists[listid].defaults.set_values[i]);
X      fprintf (f, "%s ", sys.lists[listid].defaults.set_values[i]);
X    }
X    else if (i != 2)	/* Not a password */
X      fprintf (f, "%s ", default_values[i]);
X    else
X      sprintf (password, "%d", time (0)),
X      fprintf (f, "%s ", password);
X  clean_request (params); /* Remove leading blanks from name */
X  fprintf (f, "%s\n", params);
X  fclose (f);
X  REARRANGE_SUBSCRIBERS;
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  live = (_strstr (sys.serverd_cmdoptions, "-i") != NULL);
X  chaddr = (sys.lists[listid].defaults.set_values[0][0] == EOS &&
X	    !strcmp (default_values [0], "VARIABLE")) ||
X	      (!strcmp (sys.lists[listid].defaults.set_values[0], "VARIABLE"));
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccsub), OK, FALSE, FALSE);
X  fprintf (f, "You have been added to list %s. The system has recorded\nyour \
Xaddress as %s and in order for your messages to get posted,\nyou will have to \
Xsend them from this address. If a message is ever rejected,\nplease contact \
Xthe list's owner: %s\n\n",
X	   sys.lists[listid].address, sender, sys.lists[listid].owner);
X  if (live || chaddr)  {
X    fprintf (f, "Your initial password is %s. Please change it as soon as you \
Xcan\nby issuing the following request to %s:\n\n\
X\t\tset %s password %s new-password\n\n",
X	     password, sys.server.address, sys.lists[listid].alias, password);
X    if (live)
X      fprintf (f, "This system may accept Internet TCP/IP connections for \
Xprocessing of live\nrequests, and the password will be used to give you \
Xsubscriber privileges.\nFor more information, send a 'help live' request to \
X%s.\n\n", sys.server.address);
X    if (chaddr)
X      fprintf (f, "You may change the address you \
Xare subscribed with (currently %s)\nwith the following request:\n\n\
X\t\tset %s address %s new-address\n\nassuming that you keep the same \
Xpassword.\n\n", sender, sys.lists[listid].alias, password);
X  }
X  fprintf (f, "All requests should be addressed to %s.\n", sys.server.address);
X  fclose (f);
X  strcpy (shell, "cat");
X  if ((f = fopen (welcomef, "r"))) {
X    fgets (shell, 3, f);
X    if (!strncmp (shell, "#!", 2)) {
X      fgets (shell, MAX_LINE - 2, f);
X      if (shell [strlen (shell) - 1] == '\n')
X	shell [strlen (shell) - 1] = EOS;
X    }
X    else
X      strcpy (shell, "cat");
X    fclose (f);
X  }
X  syscom ("%s %s >> %s", shell, welcomef, MAILFORWARD);
X  APPEND_TELNET ("subscribe");
X  DELIVER_MAIL (sender, COPY_OWNER (ccsub));
X}
X
X/*
X  Tell 'sender' what list(s) he/she is subscribed in.
X*/
X
Xvoid which (char *request, char *params, char *sender)
X{
X  char subscribersf [MAX_LINE];
X  char aliasesf [MAX_LINE];
X  char param [MAX_LINE];
X  FILE *f;
X  int i;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid WHICH option%s\n\
XSyntax: which\n", params), SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  fprintf (f, "%s: You are subscribed to the following lists;\nif none appear, \
Xyou are not subscribed to any:\n", sender);
X  for (i = 0; i < nlists; ++i) {
X    setup_string (subscribersf, sys.lists[i].alias, SUBSCRIBERS);
X    setup_string (aliasesf, sys.lists[i].alias, ALIASES);
X    if (subscribed (report, sender, subscribersf, NULL, NULL, aliasesf) ==
X	SUBSCRIBED)
X      fprintf (f, "%s\n", sys.lists[i].alias);
X  }
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, FALSE);
X}
X
X/*
X  Set options for user if he/she is subscribed. If the list is remote,
X  the request is forwarded. If resetting MAIL ACK/NOACK/POSTPONE from
X  DIGEST, send a partial digest.
X  Adding more SET options:
X  - In defs.h, define the new MAX_SET_OPTIONS.
X  - In global.h, define the new option in options[], the valid
X    values in values[] and the default_values[].
X  - the MAIL option should be second in the list; this assumption is made by
X    list and listserv.
X*/
X
Xvoid set (char *request, char *params, char *sender)
X{
X  FILE *f, *from, *to;
X  char error [10240];
X  char option [MAX_LINE];
X  char subscriber [MAX_LINE];
X  char oldmodes [MAX_SET_OPTIONS] [MAX_LINE];
X  char newmode [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char name [MAX_LINE];
X  char address [MAX_LINE];
X  char *blank;
X  int  i, j, index;
X  long int sig_mask;
X  BOOLEAN status, ok_to_reset_address = FALSE;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  error[0] = option[0] = moreparams[0] = RESET (newmode);
X  sscanf (params, "%s %s %s\n", option, newmode, moreparams); /* Get params */
X  upcase (option);
X  upcase (newmode);
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  if ((sys.lists[listid].defaults.set_values[0][0] == EOS &&
X       !strcmp (default_values [0], "VARIABLE")) ||
X      (!strcmp (sys.lists[listid].defaults.set_values[0], "VARIABLE")))
X    ok_to_reset_address = TRUE;
X  if (!(status = subscribed (report, sender, subscribersf, newsf, peersf,
X			     aliasesf))) {
X    sprintf (error, "%s: You are not subscribed to %s\n", sender,
X             sys.lists[listid].address);
X    if (alternate_addresses) {
X      sprintf (error + strlen (error),
X	       "\nIn addition, the system found the following \
Xaddress(es) that resemble yours.\nIf one of these is you, please resend your \
Xmessage from that one%s:\n\n",
X	       (ok_to_reset_address ?
X		", or use the\n'set <list> address' request to change the \
Xaddress you are subscribed with" :
X		""));
X      for (i = 0; alternate_addresses[i]; ++i)
X	sprintf (error + strlen (error), "%s\n", alternate_addresses[i]),
X	free ((char *) alternate_addresses[i]);
X      free ((char **) alternate_addresses);
X      alternate_addresses = NULL;
X      strcat (error, "\n");
X    }
X    reject_mail (sender, request, error, INVALID_REQ, 0);
X    return;
X  }
X  else if (status > SUBSCRIBED) { /* Notify manager */
X    NOTIFY_MANAGER ("Attempt to set mode for news or peer");
X    return;
X  }
X  if (option[0] != EOS) {
X    for (index = 0; index < MAX_SET_OPTIONS; index++)
X      if (! strcmp (option, options[index]))
X	break;
X    if (index == MAX_SET_OPTIONS) {
X      reject_mail (sender, request, tsprintf ("Invalid SET option %s\n\n\
XSyntax: set <list> [<option> <arg[s]>]\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n", option), SYNTAX_ERROR, 0);
X      return;
X    }
X    if (newmode[0] == EOS ||
X	(!strcmp (option, options[0]) && moreparams[0] == EOS) ||
X	(!strcmp (option, options[2]) && moreparams[0] == EOS)) {
X      reject_mail (sender, request, tsprintf ("Missing SET %s value\n\n\
XSyntax: set <list> [<option> <arg[s]>]\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n", options[index]), SYNTAX_ERROR, 0);
X      return;
X    }
X    if (index > 0 &&
X	!strinstr (values[index], (index == 1 || index == 3 ?
X				   newmode : moreparams))) {
X      reject_mail (sender, request, tsprintf ("Invalid SET %s value %s\n\n\
XSyntax: set <list> [<option> <arg[s]>]\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n", options[index], newmode), SYNTAX_ERROR, 0);
X      return;
X    }
X  }
X  if (moreparams[0] != EOS && strcmp (option, options[0]) &&
X      strcmp (option, options[2]) && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Too many SET parameters: %s\n\n\
XSyntax: set <list> [<option> <arg[s]>]\n\
X\toption: mail, password, address, conceal\n\
X\targ for 'mail': ack/noack/postpone/digest\n\
X\targs for 'password': <current-password> <new-password>\n\
X\targs for 'address': <current-password> <new-address>\n\
X\targ for 'conceal': yes/no\n", moreparams), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (option[0] == EOS) { /* Status inquiry */
X#if defined (__NeXT__) || defined (unknown_port)
X    syscom ("grep -i '^[ \t]*%s ' %s | \
X%s '{ printf \"%%s\", $1; for (i = 1; i <= %d; ++i) printf \" %%s\", $i; \
Xprintf \"\\n\" }' > %s",
X	    sender, subscribersf, AWK, MAX_SET_OPTIONS, OLD_SUBSCRIBERS);
X#else
X    syscom ("grep -i '^[ \t]*%s ' %s | %s -d' ' -f1-%d > %s", sender,
X	    subscribersf, CUT, MAX_SET_OPTIONS, OLD_SUBSCRIBERS);
X#endif
X    if (! one_rejection) {
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		     COPY_OWNER (ccset), OK, FALSE, FALSE);
X      fprintf (f, "Current settings are:\n\n");
X      OPEN_FILE (from, OLD_SUBSCRIBERS, "r", "set");
X      for (i = 0; i < MAX_SET_OPTIONS; i++)
X	RESET (newmode),
X	fscanf (from, "%s ", newmode),
X	fprintf (f, "%s = %s\n", options[i], newmode);
X      fflush (f);
X      fclose (from);
X      COMPLETE_TELNET (f);
X      fclose (f);
X      DELIVER_MAIL (sender, COPY_OWNER (ccset));
X      unlink (OLD_SUBSCRIBERS);
X      return;
X    }
X  }
X  /* Change of mode */
X  if (cp (subscribersf, OLD_SUBSCRIBERS))
X    exit (16);
X  OPEN_FILE (from, OLD_SUBSCRIBERS, "r", "set");
X  OPEN_FILE (to, NEW_SUBSCRIBERS, "w", "set");
X  while (!feof (from)) {
X    subscriber[0] = RESET (name);
X    if (!extract_subscriber (from, subscriber, FALSE)) {
X      NOTIFY_MANAGER_OF_INVALID_USER_ADDRESS (subscriber);
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		     COPY_OWNER (ccset), OK, FALSE, FALSE);
X      fprintf (f, "Sorry, a system error has occured. Try again later.\n");
X      COMPLETE_TELNET (f);
X      fclose (f);
X      DELIVER_MAIL (sender, COPY_OWNER (ccset));
X      fclose (from);
X      fclose (to);
X      unlink (OLD_SUBSCRIBERS);
X      unlink (NEW_SUBSCRIBERS);
X      return;
X    }
X    strcpy (oldmodes[0], subscriber);
X    for (i = 1; i < MAX_SET_OPTIONS; i++)
X      RESET (oldmodes[i]),
X      fscanf (from, "%s ", oldmodes[i]);
X    fgets (name, MAX_LINE - 2, from);
X    upcase (subscriber);
X    if (!feof (from)) {
X      if (! strcmp (sender, subscriber))
X	for (i = 0; i < MAX_SET_OPTIONS; i++) {
X	  if (i == 0) {
X	    if (!strcmp (option, options[0]))
X	      if (!ok_to_reset_address)
X		fprintf (to, "%s ", subscriber),
X		reject_mail (sender, request, "Changing of address is not \
Xallowed for this list.\n", RESTRICTED_REQ, 0);
X	      else
X		if (strcmp (newmode, oldmodes[2])) /* Invalid password */
X		  fprintf (to, "%s ", subscriber),
X		  reject_mail (sender, request,
X			       tsprintf ("Password verification failure: %s\n",
X					 newmode), SYNTAX_ERROR, 0);
X		else {
X		  sprintf (address, "From %s", moreparams);
X		  if (!extract_sender (address))
X		    fprintf (to, "%s ", subscriber),
X		    reject_mail (sender, request,
X				 tsprintf ("Invalid address: %s\n", moreparams),
X				 SYNTAX_ERROR, 0);
X		  else {
X		    sprintf (address, "^[\t ]*%s ", sender);
X		    if (cp (aliasesf, OLD_ALIASES))
X		      exit (16);
X		    REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X		    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X		    sighold (SIGINT);
X		    sighold (SIGTERM);
X#endif
X		    if (mv (NEW_ALIASES, aliasesf))
X		      exit (16);
X#ifdef bsd
X		    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X		    sigrelse (SIGINT);
X		    sigrelse (SIGTERM);
X#endif
X		    if (cp (ALIASESF, OLD_ALIASES))
X		      exit (16);
X		    REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X		    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X		    sighold (SIGINT);
X		    sighold (SIGTERM);
X#endif
X		    if (mv (NEW_ALIASES, ALIASESF))
X		      exit (16);
X#ifdef bsd
X    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X    sigrelse (SIGINT);
X    sigrelse (SIGTERM);
X#endif
X		    sprintf (address, " %s[\t ]*$", sender);
X		    if (cp (aliasesf, OLD_ALIASES))
X		      exit (16);
X		    REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X		    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X		    sighold (SIGINT);
X		    sighold (SIGTERM);
X#endif
X		    if (mv (NEW_ALIASES, aliasesf))
X		      exit (16);
X#ifdef bsd
X		    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X		    sigrelse (SIGINT);
X		    sigrelse (SIGTERM);
X#endif
X		    if (cp (ALIASESF, OLD_ALIASES))
X		      exit (16);
X		    REMOVE_ADDRESS (address, OLD_ALIASES, NEW_ALIASES);
X#ifdef bsd
X		    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X		    sighold (SIGINT);
X		    sighold (SIGTERM);
X#endif
X		    if (mv (NEW_ALIASES, ALIASESF))
X		      exit (16);
X#ifdef bsd
X		    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X		    sigrelse (SIGINT);
X		    sigrelse (SIGTERM);
X#endif
X		    unlink (OLD_ALIASES);
X		    if (echo_append (tsprintf ("%s %s", subscriber,
X					       moreparams), aliasesf))
X		      exit (16);
X		    if (echo_append (tsprintf ("%s %s", subscriber,
X					       moreparams), ALIASESF))
X		      exit (16);
X		    fprintf (to, "%s ", moreparams); /* Put new address */
X		  }
X		}
X	    else
X	      fprintf (to, "%s ", subscriber);
X	  }
X	  else if (i == 2 && !strcmp (option, options[2]) &&
X		   strcmp (newmode, oldmodes[i])) /* Invalid old password */
X	    fprintf (to, "%s ", oldmodes[i]),
X	    reject_mail (sender, request,
X			 tsprintf ("Password verification failure: %s\n", newmode),
X			 SYNTAX_ERROR, 0);
X	  else
X	    fprintf (to, "%s ", (i == index ? 
X		     (strcmp (option, options[2]) ? newmode : moreparams) :
X		     oldmodes[i]));
X	  if (strcmp (newmode, DIGEST) && strcmp (newmode, POSTPONE) &&
X	      !strcmp (oldmodes[i], DIGEST))
X	    /* Turned off DIGEST, send a partial digest */
X	    syscom ("%s %s -i%s %s", LIST, sys.lists[listid].alias,
X		   sender, sys.lists[i].cmdoptions);
X	}
X      else
X	for (i = 0; i < MAX_SET_OPTIONS; i++)
X	  fprintf (to, "%s ", oldmodes[i]);
X      clean_request (name); /* Remove leading blanks */
X      fprintf (to, "%s", name);
X    }
X  }
X  fclose (from);
X  fclose (to);
X#ifdef bsd
X    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X    sighold (SIGINT);
X    sighold (SIGTERM);
X#endif
X  if (mv (NEW_SUBSCRIBERS, subscribersf))
X    exit (16);
X#ifdef bsd
X    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X    sigrelse (SIGINT);
X    sigrelse (SIGTERM);
X#endif
X  unlink (OLD_SUBSCRIBERS);
X  if (! one_rejection) {
X    blank = request;
X    (index == 0 ? (j = 3) : (j = 4));
X    for (i = 0; i < j; i++)
X      blank = strchr (blank + 1, ' ');
X    ++blank;
X    shadow_password (blank);
X    create_header (&f, MAILFORWARD, sys.server.address, 
X		   (index == 0 ? moreparams : sender), request,
X		   COPY_OWNER (ccset), OK, FALSE, FALSE);
X    fprintf (f, "%s mode reset to %s\n", options[index],
X	     (index == 0 || index == 2 ? moreparams : newmode));
X    COMPLETE_TELNET (f);
X    fclose (f);
X    if (!index) {
X      DELIVER_MAIL (moreparams, COPY_OWNER (ccset));
X    }
X    else
X      DELIVER_MAIL (sender, COPY_OWNER (ccset));
X  }
X}
X
X/*
X  Provide user with the current list of subscribers. Peer servers are
X  also notified and they send their own compilations. If the list is
X  remote, the request is forwarded.
X  For private lists, only members may make this request.
X*/
X
Xvoid recipients (char *request, char *params, char *sender)
X{
X  char param [MAX_LINE];
X  FILE *f;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid RECIPIENTS option%s\n\
XSyntax: recipients <list> (or review <list>)\n", params), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (sys.lists[listid].options & NON_AUTO_SUB) { /* Private list */
X    if (subscribed (report, sender, subscribersf, NULL, NULL, aliasesf) ==
X	NOTSUBSCRIBED) {
X      MEMBERS_ONLY;
X      return;
X    }
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccrec), OK, FALSE, FALSE);
X  fprintf (f, "Here is the current list of non-concealed subscribers:\n\n");
X  fclose (f);
X#if defined (__NeXT__) || defined (unknown_port)
X  syscom ("%s '{ printf \"%%s\", $1; for (i = %d; i <= %d; ++i) \
Xprintf \" %%s\", $i; printf \"\\n\" }' %s | grep -i ' NO ' | \
X%s '{ printf \"%%s\", $1; for (i = 3; i <= %d; ++i) printf \" %%s\", $i; \
Xprintf \"\\n\" }' > %s",
X	  AWK, MAX_SET_OPTIONS, MAX_SET_OPTIONS + 5, subscribersf, AWK,
X	  MAX_SET_OPTIONS + 4, recipf);
X#else
X  syscom ("%s -d\" \" -f1,%d-%d %s | grep -i ' NO ' | %s -d\" \" -f1,3-%d \
X> %s", CUT, MAX_SET_OPTIONS, MAX_SET_OPTIONS + 5, subscribersf, CUT,
X	  MAX_SET_OPTIONS + 4, recipf);
X#endif
X  syscom ("%s -f %s %s >> %s", AWK, AWK_PROG, recipf, MAILFORWARD);
X  syscom ("echo Total number of subscribers: `cat %s | wc -l` \
X\"(`cat %s | wc -l | tr -d ' '` shown here)\" >> %s", subscribersf, recipf,
X	  MAILFORWARD);
X  unlink (recipf);
X  APPEND_TELNET ("recipients");
X  DELIVER_MAIL (sender, COPY_OWNER (ccrec));
X  notify_peer_servers (peersf, "review", params, sender);
X}
X
X/*
X  Provide user with general information about the list. A reminder, the
X  actual text is in the INFO_FILE. If the list is remote, the request
X  is forwarded.
X*/
X
Xvoid info (char *request, char *params, char *sender)
X{
X  char param [MAX_LINE];
X  char shell [MAX_LINE];
X  FILE *f;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid INFORMATION option%s\n\
XSyntax: information <list>\n", params), SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccinfo), OK, FALSE, FALSE);
X  fclose (f);
X  strcpy (shell, "cat");
X  if ((f = fopen (infof, "r"))) {
X    fgets (shell, 3, f);
X    if (!strncmp (shell, "#!", 2)) {
X      fgets (shell, MAX_LINE - 2, f);
X      if (shell [strlen (shell) - 1] == '\n')
X	shell [strlen (shell) - 1] = EOS;
X    }
X    else
X      strcpy (shell, "cat");
X    fclose (f);
X  }
X  syscom ("%s %s >> %s", shell, infof, MAILFORWARD);
X  APPEND_TELNET ("info");
X  DELIVER_MAIL (sender, COPY_OWNER (ccinfo));
X}
X
X/*
X  Collect and send statistics about all subscribers, by grepping through
X  HEADERS. If a user has selected particular names (asterisks are OK) then
X  give statistics about these people only. Also include a total count
X  of messages on file. Peer servers are also notified and they send their
X  own compilations. If the list is remote, the request is forwarded.
X  For private lists, only members may make this request.
X*/
X
Xvoid stats (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char junk [MAX_LINE];
X  struct stat stat_buf;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  if (sys.lists[listid].options & NON_AUTO_SUB) { /* Private list */
X    if (subscribed (report, sender, subscribersf, NULL, NULL, aliasesf) ==
X	NOTSUBSCRIBED) {
X      MEMBERS_ONLY;
X      return;
X    }
X  }
X  params [strlen (params) - 1] = EOS; /* Remove \n */
X  RESET (junk);
X  sscanf (params, "%s", junk);
X  if (junk[0] == EOS) /* No specific subscribers to report on */
X    RESET (params);
X  if (stat (headersf, &stat_buf)) {
X    NOTIFY_OF_BAD_ARCHIVE ("Sorry, no mail archive found.\n", NULL, OK);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccstat), OK, FALSE, FALSE);
X  fprintf (f, "Here are the number of messages per non-concealed subscriber:\
X\n\n");
X  fclose (f);
X  EXEC_STATS;
X  APPEND_TELNET ("stats");
X  DELIVER_MAIL (sender, COPY_OWNER (ccstat));
X  notify_peer_servers (peersf, "stat", params, sender);
X}
X
X/*
X  Exit with a shutdown status if the correct password is provided.
X*/
X
Xvoid Shutdown (char *request, char *params, char *sender)
X{
X  char passwd [MAX_LINE];
X
X  sscanf (params, "%s\n", passwd);
X  if (!strcmp (sys.server.password, passwd))
X    report_progress (report, "SHUTDOWN request accepted", TRUE),
X    gexit (6); /* Exit status of 6 signifies a shutdown request */
X  reject_mail (sender, request, tsprintf ("Unrecognized request %s\n", request),
X	       INVALID_REQ, 0);
X}
X
X/*
X  Set the global variable 'restart_sys' to TRUE if the correct password
X  is given.
X*/
X
Xvoid restart (char *request, char *params, char *sender)
X{
X  char passwd [MAX_LINE];
X
X  sscanf (params, "%s\n", passwd);
X  if (!strcmp (sys.server.password, passwd)) { 
X    report_progress (report, "RESTART request accepted\n", FALSE);
X    restart_sys = TRUE;
X    return;
X  }
X  reject_mail (sender, request, tsprintf ("Unrecognized request %s\n", request),
X	       INVALID_REQ, 0);
X}
X
X/*
X  Provide 'sender' with a list of all mailing lists served by this server,
X  as well as any remote lists known to this server.
X*/
X
Xvoid lists (char *request, char *params, char *sender)
X{
X  char param [MAX_LINE];
X  FILE *f;
X  int i, alslen, lstlen, adrlen, len, _nlists;
X  REMOTE *r = rlists, *s = rlists;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid LISTS option%s\n\
XSyntax: lists\n", params), SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (cclists), OK, FALSE, FALSE);
X  adrlen = -99;
X  for (i = 0, _nlists = 0; i < nlists; ++i) /* Get longest address */
X    if (!(sys.lists[i].options & CONCEAL_LIST)) {
X      ++_nlists;
X      if ((len = strlen (sys.lists[i].address)) > adrlen)
X	adrlen = len;
X    }
X  fprintf (f, "Here is the current active list of the %d mailing lists \
Xserved by this server:\n\n", _nlists);
X  for (i = 0; i < nlists; ++i)
X    if (!(sys.lists[i].options & CONCEAL_LIST))
X      fprintf (f, "%-*s  %s\n", adrlen, sys.lists[i].address,
X	       sys.lists[i].comment);
X
X  if (rlists) { /* List all remote lists */
X    i = 0;
X    fprintf (f, "\nIn addition, the following remote lists are known to this \
Xserver:\n\n");
X    alslen = strlen ("ALIAS");
X    adrlen = strlen ("FULL ADDRESS");
X    lstlen = strlen ("LISTSERVER ADDRESS");
X    while (s) {
X      if ((len = strlen (s->alias)) > alslen)
X	alslen = len;
X      if ((len = strlen (s->address)) > adrlen)
X        adrlen = len;
X      if ((len = strlen (s->listserv)) > lstlen)
X        lstlen = len;
X      s = s->next;
X    }
X    fprintf (f, "%-*s %-*s %-*s COMMENT\n%-*s %-*s %-*s -------\n",
X	   alslen, "ALIAS", adrlen, "FULL ADDRESS", lstlen, "LISTSERVER ADDRESS",
X	   alslen, "-----", adrlen, "------------", lstlen, "------------------");
X    while (r)
X      ++i,
X      fprintf (f, "%-*s %-*s %-*s %s\n", alslen, r->alias, adrlen,
X	       r->address, lstlen, r->listserv, r->comment),
X      r = r->next;
X    fprintf (f, "\nTotal number of remote lists: %d. Requests sent to \
Xthis server for these\nremote lists will be forwarded to the servers \
Xserving these lists.\n", i);
X  }
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (cclists));
X}
X
X/*
X  Get an index of files for the specified archive, or the master archive
X  if none specified. Private archives require a password to list their
X  files.
X
X  Adapted USER CONTRIBUTED FUNCTION: Stefan Schroer.
X*/
X
Xvoid Index (char *request, char *params, char *sender)
X{
X  FILE *f, *index, *dir, *master;
X  char archive [MAX_LINE];
X  char arch [MAX_LINE];
X  char cur_archive [MAX_LINE];
X  char line [MAX_LINE];
X  char file [MAX_LINE];
X  char fullpath [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char fullname [MAX_LINE];
X  char desc [MAX_LINE];
X  char junk [MAX_LINE];
X  char user_password [MAX_LINE];
X  char sys_password [MAX_LINE];
X  char *slash;
X  BOOLEAN found, continued, header_printed, all = FALSE;
X  int count, parts, i;
X  long int *filesizes;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  archive[0] = fullpath[0] = fullname[0] = 
X  cur_archive[0] = RESET (moreparams);
X  sscanf (params, "%s %s %s", archive, user_password, moreparams);
X  if (archive[0] == '/') /* No archive given, just password */
X    strcpy (user_password, archive),
X    RESET (archive);
X  else if (!strcmp (archive, "-ALL"))
X    all = TRUE,
X    RESET (archive);
X  if (user_password[0] == '/') /* Remove leading '/' */
X    sprintf (user_password, "%s", user_password + 1);
X  else if (!strcmp (user_password, "-ALL"))
X    RESET (user_password),
X    all = TRUE;
X  locase (archive);
X  if (moreparams[0] != EOS)
X    if (strcmp (moreparams, "-ALL")) {
X      if (!(sys.options & RELAXED_SYNTAX)) {
X	reject_mail (sender, request,
X		     tsprintf ("Too many arguments to INDEX: %s\n\n\
XSyntax: index [archive | path-to-archive] [/password]\n", moreparams),
X		     SYNTAX_ERROR, 0);
X	return;
X      }
X    }
X    else
X      all = TRUE;
X  if (archive[0] == EOS) /* Get archive */
X    strcpy (archive, DEFAULT_ARCHIVE);
X  sprintf (fullpath, "%s/%s/%s", ARCHIVE_DIR, DEFAULT_ARCHIVE, INDEX);
X  strcpy (cur_archive, archive);
X  if ((slash = strchr (cur_archive, '/')))
X    *slash = EOS;
X  while (archive[0] != EOS) { /* Check all archives specified */
X    if ((master = fopen (fullpath, "r")) == NULL) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: Sorry, no index found.\n", cur_archive,
X			     BAD_ARCHIVE);
X      return;
X    }
X    found = FALSE;
X    while (!feof (master)) { /* Look at the current index for fullpath */
X      fullpath[0] = arch[0] = RESET (line);
X      fgets (line, MAX_LINE - 2, master);
X      if (line[0] != EOS) {
X        sscanf (line, "%s %s\n", arch, fullpath);
X        locase (arch);
X        if (!strcmp (arch, cur_archive)) {
X 	  found = TRUE;
X	  break;
X        }
X      }
X    }
X    fclose (master);
X    if (!found) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: not a valid archive or path.\n", cur_archive,
X			     SYNTAX_ERROR);
X      return;
X    }
X    if ((slash = strchr (archive, '/')))
X      sprintf (archive, "%s", slash + 1); /* Move down the path */
X    else
X      sprintf (archive, "%s", archive + strlen (cur_archive));
X    strcpy (cur_archive, archive);
X    if ((slash = strchr (cur_archive, '/')))
X      *slash = EOS;
X    if (cur_archive[0] != EOS)
X      sprintf (fullpath, "%s/%s", fullpath, INDEX);
X  }
X  sscanf (params, "%s", archive);
X  sprintf (fullname, "%s/%s", fullpath, INDEX);
X  if ((index = fopen (fullname, "r")) == NULL) { /* Open index */
X    NOTIFY_OF_BAD_ARCHIVE ("Sorry, no index found in archive %s.\n", archive,
X			   BAD_ARCHIVE);
X    return;
X  }
X  slash = request;
X  while ((slash = strchr (slash + 1, '/')) && !isspace (*(slash - 1)));
X  if (slash)
X    shadow_password (slash + 1);
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccindex), OK, FALSE, FALSE);
X  count = 0;
X  while (!feof (index)) { /* Echo archive; goto archive and get DIR file */
X    fullpath[0] = archive[0] = sys_password[0] = RESET (line);
X    fgets (line, MAX_LINE - 2, index);
X    if (line[0] != EOS) {
X      sscanf (line, "%s %s %s\n", archive, fullpath, sys_password);
X      upcase (sys_password);
X      fprintf (f, "\n%s: %s ", (!count ?  "Archive" : "Subarchive"), archive);
X      if (!strncmp (fullpath, ARCHIVE_DIR, strlen (ARCHIVE_DIR)))
X	fprintf (f, "(path: %s) ", fullpath + strlen (ARCHIVE_DIR) + 1);
X      if (!count || all) { /* List files for the top level archive only */
X	fprintf (f, "-- Files:\n");
X	if (sys_password[0] != EOS && strcmp (user_password, sys_password)) {
X	  reply_code (RESTRICTED_REQ);
X	  fprintf (f, "This archive requires special privileges for access.\n");
X	  continue;
X	}
X	if (chdir (fullpath))
X	  fprintf (f, "%s: Sorry, archive out of date.\n", archive);
X	else { /* Open DIR and get file names */
X	  if ((dir = fopen (DIRF, "r")) == NULL)
X	    reply_code (BAD_ARCHIVE),
X	    fprintf (f, "Cannot obtain directory information.\n");
X	  else {
X	    while (!feof (dir)) {
X	      line[0] = junk[0] = RESET (file);
X	      fscanf (dir, "%s %d ", file, &parts);
X	      if (file[0] != EOS) {
X		if (! (filesizes = (long int *) malloc (abs (parts) * sizeof (long int))))
X		  report_progress (report, "\nIndex(): malloc() failed", TRUE),
X		  gexit (11);
X		for (i = 0; i < abs (parts); i++)
X		  fscanf (dir, "%ld ", filesizes + i);
X		fscanf (dir, "%s", junk);
X		header_printed = FALSE;
X		do { /* Get file description */
X		  RESET (desc);
X		  fgets (desc, MAX_LINE - 2, dir);
X		  if (desc [0] != EOS && desc[strlen (desc) - 1] == '\n')
X		    desc[strlen (desc) - 1] = EOS;
X		  continued = FALSE;
X		  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X		    desc[strlen (desc) - 1] = EOS,
X		    continued = TRUE;
X		  if (file[0] != EOS) {
X		    if (!header_printed) {
X		      fprintf (f, "  %s (%d part%s", file, abs (parts),
X			       (abs (parts) > 1 ? "s" : ""));
X		      for (i = 0; i < abs (parts); i++)
X			fprintf (f, ", %ld", *(filesizes + i));
X		      fprintf (f, " bytes) %s", ((desc[0] != EOS) ? "--" : ""));
X		      free ((long int *) filesizes);
X		      header_printed = TRUE;
X		    }
X		    fprintf (f, "%s\n", desc);
X		  }
X		} while (continued);
X	      }
X	    }
X	    fclose (dir);
X	  }
X	}
X      }
X      else
X	fprintf (f, "\n");
X    }
X    ++count;
X  }
X  fclose (index);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (ccindex));
X}
X
X/*
X  Send the requested file from the specified archive. The file may have been
X  split in subparts, and in this case several emails will be sent -- one
X  for each part. The user may also obtain certain parts. A password is
X  required for obtaining files from a private archive.
X
X  Adapted USER CONTRIBUTED FUNCTION: Stefan Schroer.
X*/
X
Xvoid get (char *request, char *params, char *sender)
X{
X  FILE *f, *dir, *master;
X  char archive [MAX_LINE];
X  char arch [MAX_LINE];
X  char cur_archive [MAX_LINE];
X  char archive_orig [MAX_LINE];
X  char fullpath [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char filename [MAX_LINE];
X  char fullname [MAX_LINE];
X  char dirpath [MAX_LINE];
X  char file [MAX_LINE];
X  char line [MAX_LINE];
X  char copy [MAX_LINE];
X  char sys_password [MAX_LINE];
X  char user_password [MAX_LINE];
X  char desc [MAX_LINE];
X  char subject [MAX_LINE];
X  char req [MAX_LINE];
X  char *slash, *uue_file = NULL;
X  struct stat stat_buf;
X  int i, count = 0;
X  long int *filesizes;
X  BOOLEAN found, continued, compressed_source;
X
X  strcpy (req, request);
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  archive[0] = fullpath[0] = fullname[0] = dirpath[0] =
X  filename[0] = cur_archive[0] = archive_orig[0] = sys.fax.fax_no [0] =
X    RESET (moreparams);
X  params [strlen (params) - 1] = EOS; /* Remove \n */
X  if (!strcmp (req, "FAX"))
X    fax_it = TRUE,
X    sscanf (params, "%s %s %s %s", sys.fax.fax_no, archive, filename,
X	    moreparams);
X  else
X    fax_it = FALSE,
X    sscanf (params, "%s %s %s", archive, filename, moreparams);
X  strcpy (archive_orig, archive);
X  locase (archive);
X  locase (filename);
X  if (archive[0] == EOS || filename[0] == EOS ||
X      (fax_it && sys.fax.fax_no[0] == EOS)) { /* Missing args */
X    reject_mail (sender, request, tsprintf ("%s: missing arguments\n\n\
XSyntax: %s %s<archive | path-to-archive> <file> [/password] [parts]\n", req,
X	     req, (fax_it ? "<fax-number> " : "")), SYNTAX_ERROR, 0);
X    fax_it = FALSE;
X    return;
X  }
X  if (moreparams[0] != EOS) { /* Specified password and/or parts to get */
X    if (moreparams[0] == '/') { /* Get password and any parts */
X      strcpy (user_password, moreparams);
X      strcpy (copy, user_password);
X      upcase (copy);
X      do {
X        sprintf (params, "%s", params + 1);
X      } while (strncmp (params, copy, strlen (copy)));
X      sprintf (params, "%s", params + strlen (copy));
X      RESET (moreparams);
X      sscanf (params, "%s", moreparams); /* Get parts */
X      if (moreparams[0] != EOS)
X        sprintf (params, "%s", strchr (params, moreparams[0]));
X      if (user_password[0] == '/') /* Remove leading '/' */
X        sprintf (user_password, "%s", user_password + 1);
X    }
X    else { /* No password given just parts */
X      strcpy (copy, filename);
X      upcase (copy);
X      do {
X        sprintf (params, "%s", params + 1);
X      } while (strncmp (params, copy, strlen (copy)));
X      sprintf (params, "%s", params + strlen (copy));
X      sprintf (params, "%s", strchr (params, moreparams[0]));
X    }
X  }
X  sprintf (fullpath, "%s/%s/%s", ARCHIVE_DIR, DEFAULT_ARCHIVE, INDEX);
X  strcpy (cur_archive, archive);
X  if ((slash = strchr (cur_archive, '/')))
X    *slash = EOS;
X  while (archive[0] != EOS) { /* Check all archives specified */
X    if ((master = fopen (fullpath, "r")) == NULL) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: Sorry, no index found.\n", cur_archive,
X			     BAD_ARCHIVE);
X      fax_it = FALSE;
X      return;
X    }
X    found = FALSE;
X    while (!feof (master)) { /* Look at the current index for fullpath */
X      fullpath[0] = arch[0] = sys_password[0] = RESET (line);
X      fgets (line, MAX_LINE - 2, master);
X      if (line[0] != EOS) {
X        sscanf (line, "%s %s %s\n", arch, fullpath, sys_password);
X        locase (arch);
X	upcase (sys_password);
X        if (!strcmp (arch, cur_archive)) {
X          found = TRUE;
X          break;
X        }
X      }
X    }
X    fclose (master);
X    if (!found) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: not a valid archive or path.\n", cur_archive,
X			     SYNTAX_ERROR);
X      fax_it = FALSE;
X      return;
X    }
X    if ((slash = strchr (archive, '/')))
X      sprintf (archive, "%s", slash + 1); /* Move down the path */
X    else
X      sprintf (archive, "%s", archive + strlen (cur_archive));
X    strcpy (cur_archive, archive);
X    if ((slash = strchr (cur_archive, '/')))
X      *slash = EOS;
X    if (cur_archive[0] != EOS)
X      sprintf (fullpath, "%s/%s", fullpath, INDEX);
X  }
X
X
X  if (sys_password[0] != EOS && strcmp (user_password, sys_password)) {
X    NOTIFY_OF_BAD_ARCHIVE ("Sorry, archive %s requires special privileges \
Xfor obtaining files.\n", archive_orig, RESTRICTED_REQ);
X    fax_it = FALSE;
X    return;
X  }
X  sprintf (dirpath, "%s/%s", fullpath, DIRF);
X  if ((dir = fopen (dirpath, "r")) == NULL) { /* Dir archive */
X    NOTIFY_OF_BAD_ARCHIVE ("Unable to dir archive %s\n", archive_orig,
X			   BAD_ARCHIVE);
X    fax_it = FALSE;
X    return;
X  }
X  found = FALSE;
X  while (!feof (dir)) { /* Get location and file-count of file to send */
X    file[0] = RESET (fullpath);
X    fscanf (dir, "%s %d ", file, &count);
X    if (! (filesizes = (long int *) malloc (abs (count) * sizeof (long int))))
X      report_progress (report, "\nget(): malloc() failed", TRUE),
X      gexit (11);
X    for (i = 0; i < abs (count); i++)
X      fscanf (dir, "%ld ", filesizes + i);
X    fscanf (dir, "%s", fullpath);
X    do { /* Get file description */
X      RESET (desc);
X      fgets (desc, MAX_LINE - 2, dir);
X      if (desc[0] != EOS && desc[strlen (desc) - 1] == '\n')
X        desc[strlen (desc) - 1] = EOS;
X      continued = FALSE;
X      if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X        desc[strlen (desc) - 1] = EOS,
X        continued = TRUE;
X    } while (continued);
X    if (file[0] != EOS) {
X      locase (file);
X      if (!strcmp (filename, file)) {
X	found = TRUE;
X	break;
X      }
X    }
X  }
X  fclose (dir);
X  if (!found) {
X    NOTIFY_OF_BAD_ARCHIVE ("Sorry, file %s not found in specified archive.\n",
X			   filename, SYNTAX_ERROR);
X    fax_it = FALSE;
X    return;
X  }
X  if (interactive) /* Empty out file */
X    create_header (&f, MAILFORWARD, sys.server.address, sender, subject,
X		   COPY_OWNER (ccget), OK, FALSE, FALSE),
X    fclose (f);
X  for (i = 1; i <= abs (count); i++) { /* Send all parts of the file */
X    RESET (fullname);
X    if (abs (count) > 1)
X      sprintf (fullname, "%s/%s%d", fullpath, filename, i);
X    else
X      sprintf (fullname, "%s/%s", fullpath, filename);
X    if (moreparams[0] != EOS && !requested_part (params, i))
X      continue;
X    compressed_source = FALSE;
X    if (stat (fullname, &stat_buf)) {
X      strcat (fullname, ".Z"); /* Check for compressed file */
X      if (stat (fullname, &stat_buf)) {
X	NOTIFY_OF_BAD_ARCHIVE ("Sorry, file %s not found in specified \
Xarchive.\n", filename, BAD_ARCHIVE);
X	fax_it = FALSE;
X	return;
X      }
X      compressed_source = TRUE;
X    }
X    if (request [strlen (request) - 1] == '\n')
X      request [strlen (request) - 1] = EOS;
X    sprintf (subject, "%s (%d/%d)", request, i, abs (count));
X    if (!interactive) {
X      if (count < 0)
X	syscom ("%s %s | uuencode %s > %s",
X		(compressed_source ? "zcat" : "cat"), fullname, filename,
X		(uue_file = mystrdup (tmpnam (NULL)))),
X	compressed_source = FALSE,
X	strcpy (fullname, uue_file),
X	stat (fullname, &stat_buf),
X	*(filesizes + i - 1) = stat_buf.st_size;
X      slash = subject;
X      while ((slash = strchr (slash + 1, '/')) && !isspace (*(slash - 1)));
X      if (slash)
X	shadow_password (slash + 1);
X      {
X	long int offset = 0, cnt = 0, nbytes, nsubparts;
X	char _subject[MAX_LINE], _count[32], *part_copy = NULL;
X	FILE *file = NULL;
X	struct stat stat_buf;
X
X	if (sys.options & LIMIT_FILES) {
X	  if (compressed_source)
X	    part_copy = mystrdup (tmpnam (NULL)),
X	    syscom ("zcat %s > %s", fullname, part_copy);
X	  OPEN_FILE (file, (compressed_source ? part_copy : fullname), "r",
X		     "get");
X	  fstat (fileno (file), &stat_buf);
X	}
X	while (offset < *(filesizes + i - 1)) {
X	  strcpy (_subject, subject);
X	  if (sys.options & LIMIT_FILES &&
X	      *(filesizes + i - 1) > sys.limits.files)
X	     /* nsubparts could be inaccurate if during the process of looking
X		for the nearest newlines not beyond the limit, the total number
X		of bytes backtracked is an integer multiple of the limit; that
X		can be very frequent if the limit is too small */
X	    nsubparts = (long int)
X	      ceil ((float) *(filesizes + i - 1) / (float) sys.limits.files),
X	    sprintf (_subject + strlen (_subject), " [%ld/%ld]", (++cnt),
X		     nsubparts),
X	    sprintf (_count, " (subpart %ld/%ld)", cnt, nsubparts);
X	  create_header (&f, MAILFORWARD, sys.server.address, sender, _subject,
X			 COPY_OWNER (ccget), OK, FALSE, FALSE);
X	  fprintf (f, "Archive %s, file %s.\nPart %d/%d%s, \
Xtotal size %ld bytes:\n\n",
X		   archive_orig, filename, i, abs (count), (cnt ? _count : ""),
X		   *(filesizes + i - 1));
X	  fprintf (f, "------------------------------ Cut here \
X------------------------------\n");
X	  fclose (f);
X	  if (sys.options & LIMIT_FILES) { /* Calculate nearest newline */
X	    if (*(filesizes + i - 1) > sys.limits.files) {
X	      nbytes = sys.limits.files;
X	      fseek (file, (offset + nbytes - 1 < stat_buf.st_size ?
X			    offset + nbytes - 1 : stat_buf.st_size - 1),
X		     SEEK_SET);
X	      while (fgetc (file) != '\n' && nbytes > 1)
X		--nbytes,
X		fseek (file, -2L, SEEK_CUR);
X	      if (nbytes == 1)
X		nbytes = sys.limits.files;
X	    }
X	    else
X	      nbytes = *(filesizes + i - 1);
X	  }
X	  else
X	    nbytes = *(filesizes + i - 1);
X	  CAT_FILE;
X	  offset += nbytes;
X	  echo_append ("------------------------------ Cut here \
X------------------------------", MAILFORWARD);
X	  APPEND_TELNET ("get");
X	  DELIVER_MAIL (sender, COPY_OWNER (ccget));
X	}
X	if (file) {
X	  fclose (file);
X	  if (part_copy)
X	    unlink (part_copy),
X	    free ((char *) part_copy);
X	}
X      }
X    }
X    else { /* Interactive */
X      if (fax_it)
X	syscom ("%s < %s %s > /dev/null &", sys.fax.prog,
X		MAILFORWARD, sys.fax.fax_no),
X	reply_code (OK);
X      else
X	reply_code (WRITE_TO_FILE_ASC), /* Other modes possible */
X	syscom ("%s %s >> %s", (compressed_source ? "zcat" : "cat"), fullname,
X		MAILFORWARD);
X    }
X    if (uue_file)
X      unlink (uue_file),
X      free ((char *) uue_file),
X      uue_file = NULL;
X  }
X  free ((long int *) filesizes);
X  fax_it = FALSE;
X}
X
X/*
X  Search the specified archive for matches of the supplied extended regular
X  expression.
X*/
X
Xvoid search (char *request, char *params, char *sender)
X{
X  FILE *f, *index, *dir, *master, *fp;
X  char archive [MAX_LINE];
X  char arch [MAX_LINE];
X  char cur_archive [MAX_LINE];
X  char line [MAX_LINE];
X  char file [MAX_LINE];
X  char _line [MAX_LINE];
X  char _line_copy [MAX_LINE];
X  char fullpath [MAX_LINE];
X  char regex [MAX_LINE];
X  char fullname [MAX_LINE];
X  char desc [MAX_LINE];
X  char junk [MAX_LINE];
X  char user_password [MAX_LINE];
X  char sys_password [MAX_LINE];
X  char arch_dir [MAX_LINE];
X  char *slash, *part_copy = NULL, *quote;
X  BOOLEAN continued, compressed_source, all, header_printed, found = FALSE;
X  int count, parts, i;
X  long int filesize;
X  struct stat stat_buf;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  archive[0] = fullpath[0] = fullname[0] = 
X  cur_archive[0] = RESET (regex);
X  sscanf (params, "%s %s", archive, user_password);
X  sscanf (original_params, "%s %s %s", junk, junk, regex);
X  if (archive[0] == EOS) {
X    reject_mail (sender, request, "SEARCH: missing arguments\n\n\
XSyntax: search <archive | path-to-archive> [/password] [-all] \
X<pattern>\n", SYNTAX_ERROR, 0);
X    fax_it = FALSE;
X    return;
X  }
X  locase (archive);
X  if (user_password[0] == '/') /* Remove leading '/' */
X    sprintf (user_password, "%s", user_password + 1);
X  else if (!strcmp (user_password, "-ALL"))
X    RESET (user_password),
X    all = TRUE,
X    sscanf (original_params, "%s %s %[^\n]", junk, junk, regex);
X  else
X    sscanf (original_params, "%s %[^\n]", junk, regex),
X    RESET (user_password);
X  if (regex[0] != EOS) {
X    part_copy = mystrdup (regex);
X    upcase (part_copy);
X    if (!strcmp (part_copy, "-ALL"))
X      all = TRUE,
X      sscanf (original_params, "%s %s %s %[^\n]", junk, junk, junk, regex);
X    else if (user_password[0] != EOS)
X      sscanf (original_params, "%s %s %[^\n]", junk, junk, regex);
X    free ((char *) part_copy);
X    part_copy = NULL;
X  }
X  else {
X    reject_mail (sender, request,
X		 tsprintf ("Too few arguments to SEARCH\n\n\
XSyntax: search <archive | path-to-archive> [/password] [-all] <pattern>\n"),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  clean_request (regex);
X  if (regex[0] == '\'') {
X    sprintf (regex, "%s", regex + 1);
X    if (regex[0] == EOS || !re_strcmp ("'[ \t]*$", regex, NULL)) {
X      reject_mail (sender, request, "Mismatched or misplaced quotes\n",
X		   SYNTAX_ERROR, 0);
X      return;
X    }
X    quote = strrchr (regex, '\'');
X    *quote = EOS;
X  }
X  else if (regex[0] == '\"') {
X    sprintf (regex, "%s", regex + 1);
X    if (regex[0] == EOS || !re_strcmp ("\"[ \t]*$", regex, NULL)) {
X      reject_mail (sender, request, "Mismatched or misplaced quotes\n",
X		   SYNTAX_ERROR, 0);
X      return;
X    }
X    quote = strrchr (regex, '"');
X    *quote = EOS;
X  }
X  if (regex[0] == EOS) {
X    reject_mail (sender, request, "Empty regular expression\n", SYNTAX_ERROR, 0);
X    return;
X  }
X  if (re_strcmp (regex, "", NULL) < 0) {
X    reject_mail (sender, request, "Malformed regular expression\n",
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  sprintf (fullpath, "%s/%s/%s", ARCHIVE_DIR, DEFAULT_ARCHIVE, INDEX);
X  strcpy (cur_archive, archive);
X  if ((slash = strchr (cur_archive, '/')))
X    *slash = EOS;
X  while (archive[0] != EOS) { /* Check all archives specified */
X    if ((master = fopen (fullpath, "r")) == NULL) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: Sorry, no index found.\n", cur_archive,
X			     BAD_ARCHIVE);
X      return;
X    }
X    while (!feof (master)) { /* Look at the current index for fullpath */
X      fullpath[0] = arch[0] = RESET (line);
X      fgets (line, MAX_LINE - 2, master);
X      if (line[0] != EOS) {
X        sscanf (line, "%s %s\n", arch, fullpath);
X        locase (arch);
X        if (!strcmp (arch, cur_archive)) {
X 	  found = TRUE;
X	  break;
X        }
X      }
X    }
X    fclose (master);
X    if (!found) {
X      NOTIFY_OF_BAD_ARCHIVE ("%s: not a valid archive or path.\n", cur_archive,
X			     SYNTAX_ERROR);
X      return;
X    }
X    if ((slash = strchr (archive, '/')))
X      sprintf (archive, "%s", slash + 1); /* Move down the path */
X    else
X      sprintf (archive, "%s", archive + strlen (cur_archive));
X    strcpy (cur_archive, archive);
X    if ((slash = strchr (cur_archive, '/')))
X      *slash = EOS;
X    if (cur_archive[0] != EOS)
X      sprintf (fullpath, "%s/%s", fullpath, INDEX);
X  }
X  sscanf (params, "%s", archive);
X  sprintf (fullname, "%s/%s", fullpath, INDEX);
X  if ((index = fopen (fullname, "r")) == NULL) { /* Open index */
X    NOTIFY_OF_BAD_ARCHIVE ("Sorry, no index found in archive %s.\n", archive,
X			   BAD_ARCHIVE);
X    return;
X  }
X  slash = request;
X  while ((slash = strchr (slash + 1, '/')) && !isspace (*(slash - 1)));
X  if (slash)
X    shadow_password (slash + 1);
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccindex), OK, FALSE, FALSE);
X  fprintf (f, "Matches for pattern %s ...\n", regex);
X  locase (regex);
X  count = 0;
X  while (!feof (index)) { /* Echo archive; goto archive and get DIR file */
X    fullpath[0] = archive[0] = sys_password[0] = RESET (line);
X    fgets (line, MAX_LINE - 2, index);
X    if (line[0] != EOS) {
X      sscanf (line, "%s %s %s\n", archive, fullpath, sys_password);
X      upcase (sys_password);
X      if (!count || all) { /* Search files of the top level archive only */
X	fprintf (f, "\n%s: %s ", (!count ?  "--- Archive" : "--- Subarchive"),
X		 archive);
X	if (!strncmp (fullpath, ARCHIVE_DIR, strlen (ARCHIVE_DIR)))
X	  fprintf (f, "(path: %s) ", fullpath + strlen (ARCHIVE_DIR) + 1);
X	fprintf (f, "\n");
X	if (sys_password[0] != EOS && strcmp (user_password, sys_password)) {
X	  reply_code (RESTRICTED_REQ);
X	  fprintf (f, "This archive requires special privileges for access.\n");
X	  continue;
X	}
X	if (chdir (fullpath))
X	  fprintf (f, "%s: Sorry, archive out of date.\n", archive);
X	else { /* Open DIR and get file names */
X	  if ((dir = fopen (DIRF, "r")) == NULL)
X	    reply_code (BAD_ARCHIVE),
X	    fprintf (f, "Cannot obtain directory information.\n");
X	  else {
X	    while (!feof (dir)) {
X	      line[0] = junk[0] = RESET (file);
X	      fscanf (dir, "%s %d ", file, &parts);
X	      if (file[0] != EOS) {
X		for (i = 0; i < abs (parts); i++)
X		  fscanf (dir, "%ld ", &filesize);
X		fscanf (dir, "%s", arch_dir);
X		do { /* Get file description */
X		  RESET (desc);
X		  fgets (desc, MAX_LINE - 2, dir);
X		  if (desc [0] != EOS && desc[strlen (desc) - 1] == '\n')
X		    desc[strlen (desc) - 1] = EOS;
X		  continued = FALSE;
X		  if (desc[0] != EOS && desc[strlen (desc) - 1] == '\\')
X		    desc[strlen (desc) - 1] = EOS,
X		    continued = TRUE;
X		} while (continued);
X		if (parts > 0) { /* Binaries are not searched */
X		  for (i = 0; i < parts; i++) {
X		    RESET (fullname);
X		    if (parts > 1)
X		      sprintf (fullname, "%s/%s%d", arch_dir, file, i);
X		    else
X		      sprintf (fullname, "%s/%s", arch_dir, file);
X		    compressed_source = FALSE;
X		    if (stat (fullname, &stat_buf)) {
X		      strcat (fullname, ".Z"); /* Check for compressed file */
X		      if (stat (fullname, &stat_buf))
X			continue; /* Error in archive */
X		      compressed_source = TRUE;
X		    }
X		    if (compressed_source)
X		      part_copy = mystrdup (tmpnam (NULL)),
X		      syscom ("zcat %s > %s", fullname, part_copy);
X		    OPEN_FILE (fp, (compressed_source ? part_copy :
X				    fullname), "r", "search");
X		    header_printed = FALSE;
X		    while (!feof (fp)) {
X		      RESET (_line);
X		      fgets (_line, MAX_LINE - 2, fp);
X		      strcpy (_line_copy, _line);
X		      if (_line_copy[0] != EOS &&
X			  _line_copy[strlen (_line_copy) - 1] == '\n')
X			_line_copy[strlen (_line_copy) - 1] = EOS;
X		      locase (_line);
X		      if (_line[0] != EOS &&
X			  re_strcmp (regex, _line, NULL) > 0) {
X			if (!header_printed)
X			  if (parts > 1)
X			    fprintf (f, "\n>>> File %s, part %d:\n", file, i);
X			  else
X			    fprintf (f, "\n>>> File %s:\n", file);
X			fprintf (f, "%s\n", _line_copy);
X			header_printed = TRUE;
X		      }
X		    }
X		    if (header_printed)
X		      fprintf (f, "<<< End of matches in file %s\n", file);
X		    fclose (fp);
X		    if (part_copy)
X		      unlink (part_copy),
X		      free ((char *) part_copy),
X		      part_copy = NULL;
X		  }
X		}
X	      }
X	    }
X	    fclose (dir);
X	  }
X	}
X      }
X    }
X    ++count;
X  }
X  fclose (index);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (ccindex));
X}
X
X/*
X  Give specific information about this release.
X*/
X
Xvoid release (char *request, char *params, char *sender)
X{
X  char param [MAX_LINE];
X  FILE *f;
X
X  sprintf (request + strlen (request), "%s", params); /* Used as a subject */
X  RESET (param);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS && !(sys.options & RELAXED_SYNTAX)) {
X    reject_mail (sender, request, tsprintf ("Invalid RELEASE option%s\n\
XSyntax: release\n", params), SYNTAX_ERROR, 0);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		 COPY_OWNER (ccrelease), OK, FALSE, FALSE);
X  fprintf (f, "UNIX ListServer, version %s\n", VERSION);
X  fprintf (f, "Last update: %s\nManager: %s\nListServer address: %s\n",
X	   UPDATE_DATE, sys.manager, sys.server.address);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, COPY_OWNER (ccrelease));
X}
X
X/*
X  Forward a 'request' to all servers handling peer lists.
X*/
X
Xvoid notify_peer_servers (char *file, char *request, char *params, char *sender)
X{
X  FILE *f, *mail;
X  char *mail_method;
X  char email [MAX_LINE];
X  char mode [MAX_LINE];
X  char alias [MAX_LINE];
X  char listserv [MAX_LINE];
X  char servers [MAX_LINE];
X  char subject [MAX_LINE];
X
X  if (peer_server_request || do_not_notify_peer_server)
X    return;
X  OPEN_FILE (f, file, "r", "notify_peer_servers");
X  sprintf (subject, "%s%s", psr_string, sys.server.address);
X  RESET (servers);
X  if (sys.options & USE_ENV_VAR) {
X    if ((mail_method = (char *) malloc ((10 + strlen (sys.mail.env_var) +
X					 strlen (sender) +
X					 strlen (sys.mail.mail_prog))
X					* sizeof (char))) == NULL)
X      report_progress (report, "\nnotify_peer_servers(): malloc() failed",
X		       TRUE),
X      gexit (11);
X    sprintf (mail_method, "env - %s=%s %s ", sys.mail.env_var,
X	     sender, sys.mail.mail_prog);
X  }
X  else {
X    if ((mail_method = (char *) malloc ((strlen (sys.mail.method) + 1) 
X					* sizeof (char))) == NULL)
X      report_progress (report, "\nnotify_peer_servers(): malloc() failed",
X		       TRUE),
X      gexit (11);
X    strcpy (mail_method, sys.mail.method);
X  }
X  while (!feof (f)) {
X    email[0] = mode[0] = alias[0] = RESET (listserv);
X    fscanf (f, "%s %s %s %s\n", email, mode, alias, listserv);
X    if (email[0] != EOS) { /* Send mail to peer server */
X      sprintf (servers + strlen (servers), "%s\n", listserv);
X      create_header (&mail, MAILFORWARD, sender, listserv, 
X		     subject, FALSE, OK, FALSE, FALSE);
X      fprintf (mail, "%s %s %s\n", request, alias, params);
X      COMPLETE_TELNET (mail);
X      fclose (mail);
X      if (sys.options & USE_SYSMAIL)
X	sysmail (MAILFORWARD);
X      else
X	syscom ("%s %s < %s", mail_method,
X		(((sys.options & USE_TELNET) == 0) ? locase (listserv) : ""),
X		MAILFORWARD);
X    }
X  }
X  if (servers[0] != EOS) { /* Notify sender as well */
X    create_header (&mail, MAILFORWARD, sys.server.address, sender, 
X		   tsprintf ("Notification from %s", sys.server.address),
X		   FALSE, OK, FALSE, FALSE);
X    fprintf (mail, "%s: Your request has been forwarded to the following peer \
Xservers,\nwho will forward you with a copy of their own results:\n\n%s", 
Xupcase (request), servers);
X    COMPLETE_TELNET (mail);
X    fclose (mail);
X    if (sys.options & USE_SYSMAIL)
X      sysmail (MAILFORWARD);
X    else
X      syscom ("%s %s < %s", mail_method,
X	      (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X	      MAILFORWARD);
X  }
X  free ((char *) mail_method);
X  fclose (f);
X}
X
X/*
X  Internal request generated by catmail; the text that follows is sent
X  to 'sender'. This request is generated only for moderated lists.
X*/
X
Xvoid notify (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char line [MAX_LINE];
X
X  create_header (&f, MAILFORWARD, sys.server.address, sender, "Notification",
X		 FALSE, OK, FALSE, FALSE);
X  RESET (line);
X  while (!feof (mail) && /* Copy till eof or next message */
X	 (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE))))
X    fputs (line, f),
X    RESET (line),
X    fgets (line, MAX_LINE - 2, mail);
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, FALSE);
X  if (!strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))
X    fseek (mail, -strlen (line), SEEK_CUR); /* Move back to beginning */
X}
X
X/*
X  Approve a message for a moderated list. The owner has supplied message
X  a tag for a message approved, so look for it in the moderated file,
X  and append it to the mail file, then remove it from the moderated file.
X*/
X
Xvoid approve (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char req [MAX_LINE];
X  char line [MAX_LINE];
X  char prev_line [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char match [MAX_LINE];
X  FILE *f, *moderated, *mail;
X  int lfd = 2, lfd2 = 2, tag, tag_to_approve = 0;
X  long int sig_mask;
X  BOOLEAN copy = FALSE;
X
X  moreparams[0] = req[0] = RESET (password);
X  strcpy (req, request);
X  sscanf (params, "%s %d %s", password, &tag_to_approve, moreparams);
X  shadow_password (params);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X	   params);
X  if (!owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X    NOT_LIST_OWNER; /* Hacker attack */
X    return;
X  }
X  if (password[0] == EOS) {
X    reject_mail (sender, request,
X		 tsprintf ("Missing password for %s request\n\n\
XSyntax: approve <list> <password> <tag>\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.lists[listid].password)) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid password for %s request\n", req),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (tag_to_approve == 0) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid or missing tag number for %s request\n\n\
XSyntax: approve <list> <password> <tag>\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (moreparams[0] != EOS) {
X    reject_mail (sender, request,
X		 tsprintf ("Too many tag numbers to %s request\n\n\
XSyntax: approve <list> <password> <tag>\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (list_mail_f, O_RDWR | O_CREAT, 416, TRUE)) < 0)
X    switch (lfd) {
X    case CANT_OPEN:
X      report_progress (report, 
X		       tsprintf ("\nCould not stat file %s", list_mail_f),
X		       TRUE);
X      gexit (1);
X    case CANT_LOCK:
X      CANNOT_LOCK_FILE (list_mail_f);
X      return;
X    }
X  if ((lfd2 = lock_file (list_moderated_f, O_RDWR | O_CREAT, 416, TRUE)) < 0)
X    switch (lfd2) {
X    case CANT_OPEN:
X      report_progress (report,
X		       tsprintf ("\nCould not stat file %s", list_moderated_f),
X		       TRUE);
X      gexit (1);
X    case CANT_LOCK:
X      unlock_file (lfd);
X      CANNOT_LOCK_FILE (list_moderated_f);
X      return;
X    }
X#endif
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  OPEN_FILE (moderated, list_moderated_f, "r", "approve");
X  OPEN_FILE (mail, list_mail_f, "a", "approve");
X  prev_line[0] = RESET (line);
X  while (!feof (moderated)) {
X    if (!strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)) &&
X	copy)
X      break; /* We are at the beginning of another message; stop copying */
X    strcpy (match, "\\1");
X    if (re_strcmp (MESSAGE_TAG, line, match) > 0)  { /* Get tag # */
X      sprintf (line, "%s", line + strlen (match));
X      sscanf (line, "%d", &tag);
X      RESET (line); /* Blow away Message-Tag: */
X      if (tag_to_approve == tag)
X	copy = TRUE;
X    }
X    if (copy && prev_line[0] != EOS)
X      fprintf (mail, "%s", prev_line);
X    strcpy (prev_line, line);
X    RESET (line);
X    fgets (line, MAX_LINE - 2, moderated);
X  }
X  if (copy && prev_line[0] != EOS)
X    fprintf (mail, "%s", prev_line);
X  fclose (moderated);
X  fclose (mail);
X  if (!copy) { /* Message to be approved not found */
X    NO_SUCH_MESSAGE_TAG (tag_to_approve);
X    goto abort;
X  }
X  remove_msg (list_moderated_f, tag_to_approve, report);
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  fprintf (f, "Your request was successfully completed.\n");
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, FALSE);
X  abort: ;
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X  unlock_file (lfd2);
X#endif
X}
X
X/*
X  Discard a message from a moderated list.
X*/
X
Xvoid discard (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char req [MAX_LINE];
X  char moreparams [MAX_LINE];
X  FILE *f;
X  int tag_to_discard = 0, lfd = 2;
X  long int sig_mask;
X
X  moreparams[0] = req[0] = RESET (password);
X  strcpy (req, request);
X  sscanf (params, "%s %d %s", password, &tag_to_discard, moreparams);
X  shadow_password (params);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params);
X  if (!owner_listed (OWNERSF, sender, sys.lists[listid].alias, report)) {
X    NOT_LIST_OWNER; /* Hacker attack */
X    return;
X  }
X  if (password[0] == EOS) {
X    reject_mail (sender, request, tsprintf ("Missing password for %s request\n\n\
XSyntax: discard <list> <password> <tag>\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.lists[listid].password)) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid password for %s request\n", req),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (tag_to_discard == 0) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid or missing tag number for %s request\n\n\
XSyntax: discard <list> <password> <tag>\n", req),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  if (moreparams[0] != EOS) {
X    reject_mail (sender, request,
X		 tsprintf ("Too many tag numbers to %s request\n\n\
XSyntax: discard <list> <password> <tag>\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (list_moderated_f, O_RDWR | O_CREAT, 416, TRUE)) < 0)
X    switch (lfd) {
X    case CANT_OPEN:
X      report_progress (report,
X		       tsprintf ("\nCould not stat file %s", list_moderated_f),
X		       TRUE);
X      gexit (1);
X    case CANT_LOCK:
X      CANNOT_LOCK_FILE (list_moderated_f);
X      return;
X    }
X#endif
X#ifdef bsd
X  sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X  sighold (SIGINT);
X  sighold (SIGTERM);
X#endif
X  if (!remove_msg (list_moderated_f, tag_to_discard, report)) {
X#ifdef bsd
X    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X    sigrelse (SIGINT);
X    sigrelse (SIGTERM);
X#endif
X    NO_SUCH_MESSAGE_TAG (tag_to_discard);
X    goto abort;
X  }
X#ifdef bsd
X  sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X  sigrelse (SIGINT);
X  sigrelse (SIGTERM);
X#endif
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  fprintf (f, "Your request was successfully completed.\n");
X  COMPLETE_TELNET (f);
X  fclose (f);
X  DELIVER_MAIL (sender, FALSE);
X  abort: ;
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X#endif
X}
X
X/*
X  Execute a program and reply with the output from stdout and stderr.
X*/
X
Xvoid execute (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char req [MAX_LINE];
X  char *prog = NULL;
X  FILE *f;
X  struct stat stat_buf;
X
X  req[0] = RESET (password);
X  strcpy (req, request);
X  sscanf (params, "%s ", password);
X  shadow_password (original_params);
X  sprintf (request + strlen (request), "%s", original_params);
X  if (password[0] == EOS) {
X    reject_mail (sender, request,
X		 tsprintf ("Missing password for %s request\n\n\
XSyntax: execute <password> #<prog> [args]\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.server.password)) {
X    reject_mail (sender, request, tsprintf ("Invalid password for %s request\n",
X					    req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if ((prog = strchr (original_params, '#')) == NULL) {
X    reject_mail (sender, request,
X		 tsprintf ("Missing '#' for %s request\n\n\
XSyntax: execute <password> #<prog> [args]\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  original_params [strlen (original_params) - 1] = EOS; /* Remove \n */
X  syscom ("%s > %s 2> %s", prog + 1, STDOUT, STDERR);
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  if (!stat (STDOUT, &stat_buf) && stat_buf.st_size > 0) {
X    fprintf (f, "Output from stdout:\n");
X    fclose (f);
X    cat_append (STDOUT, MAILFORWARD);
X    APPEND_TELNET ("execute");
X    DELIVER_MAIL (sender, FALSE);
X  }
X  if (!stat (STDERR, &stat_buf) && stat_buf.st_size > 0) {
X    if (interactive) /* In case execute is ever allowed for live connections */
X      if (stat (STDOUT, &stat_buf))
X	create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		       FALSE, OK, FALSE, FALSE);
X      else
X	f = fopen (MAILFORWARD, "a");
X    else
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		     FALSE, OK, FALSE, FALSE);
X    fprintf (f, "Output from stderr:\n");
X    fclose (f);
X    cat_append (STDERR, MAILFORWARD);
X    APPEND_TELNET ("execute");
X    DELIVER_MAIL (sender, FALSE);
X  }
X  unlink (STDOUT);
X  unlink (STDERR);
X}
X
X/*
X  Execute a UNIX command allowed for the specified list by members of the list,
X  and receive the output from stdout and/or stderr.
X*/
X
Xvoid unix_cmd (char *request, char *params, char *sender)
X{
X  char password [MAX_LINE];
X  char req [MAX_LINE];
X  char prog [10240], *_prog, *args [10], *blank;
X  FILE *f;
X  struct stat stat_buf;
X  UNIX_CMDS *unix_cmd = sys.lists[listid].unix_cmds;
X  int nargs;
X  long int sig_mask;
X
X  if (matched_rlists) { /* Request for a remote list; notify sender */
X    NOTIFY_OF_REQUEST_FORWARDING;
X    FORWARD_REQUEST;
X    return;
X  }
X  req[0] = RESET (password);
X  strcpy (req, request);
X  sscanf (params, "%s ", password);
X  _prog = original_params;
X  while (*_prog != EOS && isspace (*_prog))	/* Skip over list */
X    ++_prog;
X  while (*_prog != EOS && !isspace (*_prog))	/* Get to password */
X    ++_prog;
X  shadow_password (_prog);
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           _prog);
X  if (subscribed (report, sender, subscribersf, NULL, NULL,
X		  aliasesf) == NOTSUBSCRIBED) {
X    MEMBERS_ONLY;
X    return;
X  }
X  if (password[0] == EOS) {
X    create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		   OK, FALSE, FALSE);
X    fprintf (f, "You may run the following commands:\n\n");
X    while (unix_cmd)
X      fprintf (f, "%s (%s)\n", unix_cmd->name, unix_cmd->comment),
X      unix_cmd = unix_cmd->next;
X    COMPLETE_TELNET (f);
X    fclose (f);
X    DELIVER_MAIL (sender, FALSE);
X    return;
X  }
X  if (!sys.lists[listid].unix_cmds) {
X    reject_mail (sender, request, tsprintf ("No commands to execute for %s \
Xrequest\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if (strcmp (password, sys.lists[listid].unix_cmds->password)) {
X    reject_mail (sender, request, tsprintf ("Invalid password for %s request\n",
X					    req), SYNTAX_ERROR, 0);
X    return;
X  }
X  if ((blank = strchr (original_params, '`')) ||
X      (blank = strchr (original_params, '|')) ||
X      (blank = strchr (original_params, '<')) ||
X      (blank = strchr (original_params, '>')) ||
X      (blank = strchr (original_params, ':'))) {
X    reject_mail (sender, request,
X		 tsprintf ("Invalid character %c in request.\n", *blank),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  clean_request (_prog); /* Skip leading blanks */
X  while (*_prog != EOS && *_prog == 'X') ++_prog;
X  clean_request (_prog); /* Skip leading blanks */
X  if (*_prog == EOS) {
X    reject_mail (sender, request, tsprintf ("Missing command to run for %s \
Xrequest\n\n\
XSyntax: run <list> [<password> <cmd> [args]]\n", req), SYNTAX_ERROR, 0);
X    return;
X  }
X  sscanf (_prog, "%s ", prog);
X  upcase (prog);
X  while (unix_cmd) {
X    if (!strcmp (unix_cmd->name, prog)) {
X      strcpy (prog, unix_cmd->cmd);
X      break;
X    }
X    unix_cmd = unix_cmd->next;
X  }
X  if (!unix_cmd) {
X    reject_mail (sender, request, tsprintf ("Unknown command %s\n", prog),
X		 SYNTAX_ERROR, 0);
X    return;
X  }
X  original_params [strlen (original_params) - 1] = EOS; /* Remove \n */
X  while (*_prog != EOS && isspace (*_prog))	/* Skip over cmd */
X    ++_prog;
X  while (*_prog != EOS && !isspace (*_prog))	/* Get to arguments */
X    ++_prog;
X  while (*_prog != EOS && isspace (*_prog))
X    ++_prog;
X  nargs = 0;
X  while (*_prog != EOS && nargs < 9) {
X    if (!(args[nargs] = (char *) malloc ((strlen (_prog) + 1) * sizeof (char))))
X      report_progress (report, "\nunix_cmd(): malloc() failed", TRUE),
X      gexit (11);
X    strcpy (args[nargs], _prog);
X    if ((blank = strchr (args[nargs++], ' ')))
X      *blank = EOS;
X    while (*_prog != EOS && !isspace (*_prog))
X      ++_prog;
X    while (*_prog != EOS && isspace (*_prog))	/* Get to next arg */
X      ++_prog;
X  }
X  _prog = prog;
X  while (*_prog != EOS && (_prog = strchr (_prog, '$')))
X    if (*(_prog + 1) == '*') 	/* Insert all user arguments */
X      if (nargs)
X	_prog += insert_word (_prog, args, nargs, 0, 2);
X      else
X	*_prog = ' ',
X	*(_prog + 1) = ' ',
X	_prog += 2;
X    else if (isdigit (*(_prog + 1)) && *(_prog + 1) > '0')
X      if (nargs && ((int) *(_prog + 1) - '0') <= nargs)
X	_prog += insert_word (_prog, args, 1, (int) *(_prog + 1) - '0' - 1, 2);
X      else
X	*_prog = ' ',
X	*(_prog + 1) = ' ',
X	_prog += 2;
X    else
X      ++_prog;
X  while (nargs)
X    free ((char *) args[--nargs]);
X#ifdef bsd
X    sig_mask = sigblock (sigmask (SIGINT) | sigmask (SIGTERM));
X#elif defined (svr4) || defined (svr3)
X    sighold (SIGINT);
X    sighold (SIGTERM);
X#endif
X  syscom ("%s > %s 2> %s", prog, STDOUT, STDERR);
X#ifdef bsd
X    sigsetmask (sig_mask);
X#elif defined (svr4) || defined (svr3)
X    sigrelse (SIGINT);
X    sigrelse (SIGTERM);
X#endif
X  create_header (&f, MAILFORWARD, sys.server.address, sender, request, FALSE,
X		 OK, FALSE, FALSE);
X  if (!stat (STDOUT, &stat_buf) && stat_buf.st_size > 0) {
X    fprintf (f, "Output from stdout:\n");
X    fclose (f);
X    cat_append (STDOUT, MAILFORWARD);
X    APPEND_TELNET ("execute");
X    DELIVER_MAIL (sender, FALSE);
X  }
X  if (!stat (STDERR, &stat_buf) && stat_buf.st_size > 0) {
X    if (interactive)
X      if (stat (STDOUT, &stat_buf))
X	create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		       FALSE, OK, FALSE, FALSE);
X      else
X	f = fopen (MAILFORWARD, "a");
X    else
X      create_header (&f, MAILFORWARD, sys.server.address, sender, request,
X		     FALSE, OK, FALSE, FALSE);
X    fprintf (f, "Output from stderr:\n");
X    fclose (f);
X    cat_append (STDERR, MAILFORWARD);
X    APPEND_TELNET ("execute");
X    DELIVER_MAIL (sender, FALSE);
X  }
X  unlink (STDOUT);
X  unlink (STDERR);
X}
X
X/*
X  Initialize the commands[].
X*/
X
Xvoid init_commands ()
X{
X  COMMAND (0, "HELP", 0x1, help);
X  COMMAND (1, "SET", 0x2, set);
X  COMMAND (2, "SUBSCRIBE", 0x4, subscribe);
X  COMMAND (3, "UNSUBSCRIBE", 0x8, unsubscribe);
X  COMMAND (4, "SIGNOFF", 0x8, unsubscribe);
X  COMMAND (5, "RECIPIENTS", 0x10, recipients);
X  COMMAND (6, "REVIEW", 0x10, recipients);
X  COMMAND (7, "INFORMATION", 0x20, info);
X  COMMAND (8, "STATISTICS", 0x40, stats);
X  COMMAND (9, "SHUTDOWN", 0x80, Shutdown);
X  COMMAND (10, "RESTART", 0x100, restart);
X  COMMAND (11, "LISTS", 0x200, lists);
X  COMMAND (12, "INDEX", 0x400, Index);
X  COMMAND (13, "GET", 0x800, get);
X  COMMAND (14, "RELEASE", 0x1000, release);
X  COMMAND (15, "WHICH", 0x2000, which);
X  COMMAND (16, "SYSTEM", 0x4000, System);
X  COMMAND (17, "REPORTS", 0x8000, get_sys_files);
X  COMMAND (18, "EDIT", 0x10000, get_sys_files);
X  COMMAND (19, "PUT", 0x20000, put);
X  COMMAND (20, "NOTIFY", 0x40000, notify);
X  COMMAND (21, "APPROVE", 0x80000, approve);
X  COMMAND (22, "DISCARD", 0x100000, discard);
X  COMMAND (23, "EXECUTE", 0x200000, execute);
X  COMMAND (24, "RUN", 0x400000, unix_cmd);
X  COMMAND (25, "FAX", 0x800000, get);
X  COMMAND (26, "SEARCH", 0x1000000, search);
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: listserv [-1] [-e] [-i] [-n] [-a <LIST_ALIAS>] \
X[-c <LIST_ALIAS>] {[-r <request>]}* {[-d <request>]}* {[-b <request>]}* [-B] \
X[-D]\n\
X-1: Execute only once.\n\
X-e: Echo reports to the screen.\n\
X-i: Interactive mode (do not send mail).\n\
X-n: Do not notify peer servers.\n\
X-a: Turn off automatic subscription for the specified list.\n\
X-c: Conceal the specified list from LISTS requests.\n\
X-r: Set restriction on 'request'.\n\
X-d: Disable 'request'.\n\
X-b: Batch 'request'.\n\
X-B: Process the batch queue.\n\
X-D: Turn debug on.\n");
X  exit (3);
X}
X
Xvoid server_config (char *alias)
X{
X  setup_string (list_mail_f, alias, LIST_MAIL_FILE);
X  setup_string (list_moderated_f, alias, LIST_MODERATED_F);
X  setup_string (infof, alias, INFO_FILE);
X  setup_string (recipf, alias, RECIP_FILE);
X  setup_string (welcomef, alias, WELCOME_FILE);
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (aliasesf, alias, ALIASES);
X  setup_string (newsf, alias, NEWSF);
X  setup_string (peersf, alias, PEERS);
X  setup_string (headersf, alias, HEADERS);
X  setup_string (ignoredf, alias, IGNORED);
X  setup_string (aliases_timestampf, alias, ALIASES_TIMESTAMPF);
X  setup_string (ignored_timestampf, alias, IGNORED_TIMESTAMPF);
X  setup_string (info_timestampf, alias, INFO_TIMESTAMPF);
X  setup_string (subscribers_timestampf, alias, SUBSCRIBERS_TIMESTAMPF);
X  setup_string (welcome_timestampf, alias, WELCOME_TIMESTAMPF);
X  setup_string (news_timestampf, alias, NEWS_TIMESTAMPF);
X  setup_string (peers_timestampf, alias, PEERS_TIMESTAMPF);
X}
X
X/*
X  Graceful exit. Remove pid file.
X*/
X
Xint gexit (int exitcode)
X{
X  unlink (PID_SERVER);
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X#endif
X  exit (exitcode);
X}
*-*-END-of-src/listserv.c-*-*
echo x - src/misc.c
sed 's/^X//' >src/misc.c <<'*-*-END-of-src/misc.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                      GENERAL PURPOSE FUNCTIONS                           |
X  |                                                                          |
X  |                             Version 3.0                                  |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X  && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#include <time.h>
X#include <string.h>
X#include <signal.h>
X#include <ctype.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <signal.h>
X#include <sys/wait.h>
X#include <math.h>
X#include "defs.h"
X#include "struct.h"
X
X#ifndef WAIT3_NEEDS_UNION
X# if defined (sequent) || defined (unknown_port)
X#  ifdef WEXITSTATUS
X#   undef WEXITSTATUS
X#  endif
X# endif
X#endif
X
X#ifndef WEXITSTATUS
X# define  WEXITSTATUS(stat)	((int)(((stat)>>8)&0377))
X#endif
X
Xextern  COMMANDS commands[MAX_COMMANDS];
Xextern  REMOTE *rlists, *matched_rlists;
Xextern  BOOLEAN extract_sender (char *);
Xextern  char *options [MAX_SET_OPTIONS];
Xextern  char *values [MAX_SET_OPTIONS];
X
X#if !defined (__NeXT__) && !defined (__osf__) && !defined (_AIX)
Xextern long int atoi (char *);
X#else
Xextern int atoi (const char *);
X#endif
Xextern BOOLEAN subscribed (FILE *, char *, char *, char *, char *, char *);
Xextern void process_message (char *, char *, BOOLEAN, BOOLEAN);
Xextern int re_strcmp (char *, char *, char *);
Xextern int gexit (int);
Xextern int mv (char *, char *);
X
X#ifdef __STDC__
X# include "ansi/misc.h"
X# include <stdarg.h>
Xint 	syscom (char *, ...);
Xextern  char *tsprintf (char *, ...);
X#else
X# include "nonansi/misc.h"
X# include <varargs.h>
Xint     syscom ();
Xextern  char *tsprintf ();
X#endif
Xint	sys_config (FILE *, SYS *);
Xvoid    config_owner_prefs (SYS *, int, FILE *);
Xchar    *locase (char *);
Xchar    *upcase (char *);
Xvoid    shadow_password (char *);
Xvoid    report_progress (FILE *, char *, int);
Xvoid    distribute (FILE *, void (*)(char *, char *, BOOLEAN, BOOLEAN), FILE *,
X		    char *, char *, char *, char *);
XBOOLEAN strinstr (char *, char *);
Xchar    *extract_filename (char *);
Xint     _getopt (int, char **, char *);
Xchar    *clean_name (char *);
Xvoid    clean_request (char *);
Xvoid	get_list_name (char *, char *);
Xvoid    free_remote_matched (REMOTE **);
Xint	get_list_id (char *, SYS *, int);
XREMOTE  *check_remote (REMOTE *, char *);
Xvoid	setup_string (char *, char *, char *);
Xchar	*_strstr (char *, char *);
Xvoid    shrink (char *);
XBOOLEAN requested_part (char *, int);
Xvoid    free_remote (REMOTE **);
Xint	my_system (char *);
XBOOLEAN remove_msg (char *, int, FILE *);
Xvoid    read_params (char *, char *, char *, FILE *, FILE *);
Xint	lock_file (char *, int, int, BOOLEAN);
Xvoid	unlock_file (int);
Xlong int write_to_fd (int, char *, long int);
XBOOLEAN mkdir1 (char *, char *, char *);
Xint	otoi (char *);
XBOOLEAN get_field (char **, char, char *);
XBOOLEAN make_indexes (char *, char *, char *, char *, char *);
Xint	insert_word (char *, char **, int, int, int);
Xchar    *skip_to_word (char *, int);
Xchar    *mystrdup (char *);
X
X/*
X  Execute a system request.
X*/
X
X#ifdef __STDC__
Xint syscom (char *control, ...)
X#else
Xint syscom (control, va_alist)
Xchar *control;
Xva_dcl
X#endif
X{
X  char command [10240], *ambersand;
X  int mask;
X  extern SYS sys;
X  extern BOOLEAN tty_echo;
X  va_list ap;
X  int status;
X  FILE *f;
X  long int time_is = 0;
X  struct tm *t;
X
X#ifdef __STDC__
X  va_start (ap, control);
X#else
X  va_start (ap);
X#endif
X  RESET (command);
X  vsprintf (command, control, ap);
X  va_end (ap);
X  if (!_strstr (command, " 2>")) {
X    sprintf (command + strlen (command), " 2>> %s", WARNING);
X    if ((ambersand = _strstr (command, " &")))
X      *(ambersand + 1) = ' ',
X      strcat (command, " &");
X  }
X#ifdef GO_INTERACTIVE
X# if defined (bsd)
X#  ifdef SIGCLD
X	  mask = sigblock (sigmask (SIGCLD));
X#  elif defined (SIGCHLD)
X	  mask = sigblock (sigmask (SIGCHLD));
X#  endif
X# elif (defined (svr4) || defined (svr3)) && !defined (stellar) && \
X  !defined (M_UNIX) && !defined (M_XENIX) && !defined (sco) && \
X  !defined (unknown_port)
X#  ifdef SIGCLD
X	  sighold (SIGCLD);
X#  elif defined (SIGCHLD)
X	  sighold (SIGCHLD);
X#  endif
X# endif
X#endif
X  if ((sys.options & USE_MY_SYSTEM) == 0)
X    status = system (command);
X  else
X    status = my_system (command);
X#ifdef GO_INTERACTIVE
X# if defined (bsd) && (defined (SIGCLD) || defined (SIGCHLD))
X    sigsetmask (mask);
X# elif (defined (svr3) || defined (svr4)) && !defined (stellar) && \
X  !defined (M_UNIX) && !defined (M_XENIX) && !defined (sco) && \
X  !defined (unknown_port)
X# ifdef SIGCLD
X    sigrelse (SIGCLD);
X#  elif defined (SIGCHLD)
X    sigrelse (SIGCHLD);
X#  endif
X# endif
X#endif
X  if (status > 0) {
X    if ((f = fopen (WARNING, "a")) != NULL)
X      fprintf (f, "\nWARNING: System call exit status %d: %s\n",
X	       WEXITSTATUS (status), command),
X      time (&time_is),
X      t = localtime (&time_is),
X      fprintf (f, "Time/Date: %2d:%.2d:%.2d, %2d/%.2d/%2d\n",
X               t->tm_hour, t->tm_min, t->tm_sec, t->tm_mon + 1, t->tm_mday,
X               t->tm_year),
X      fclose (f);
X    if (tty_echo)
X      printf ("\nWARNING: System call exit status %d: %s\n",
X	      WEXITSTATUS (status), command);
X  }
X  return status;
X}
X
X/*
X  Initialize the 'sys' structure from the CONFIG file. It returns the number
X  of lists in the system.
X*/
X
Xint sys_config (FILE *report, SYS *sys)
X{
X  FILE *config;
X  char cmd [MAX_LINE];
X  char args [MAX_LINE];
X  char arg [MAX_LINE];
X  char line [MAX_LINE];
X  char *comment, *cmdarg, *start, *end;
X  int nlists = 0, i, j, k, id;
X  BOOLEAN notok, found;
X  REMOTE *remote;
X  PRECIOUS_HEADER *header;
X  UNIX_CMDS *unix_cmd;
X
X  for (i = 0; i < MAX_LISTS; i++)
X    sys->lists[i].digest_lines = 1000,
X    sys->lists[i].digest_hours = 24,
X    sys->lists[i].disabled_commands = 0,
X    sys->lists[i].owner_prefs = 0,
X    sys->lists[i].header = NULL,
X    sys->lists[i].unix_cmds = NULL,
X    sys->lists[i].options = 0,
X    sys->lists[i].max_messages = 0,
X    RESET (sys->lists[i].alias),
X    RESET (sys->lists[i].address),
X    RESET (sys->lists[i].password),
X    RESET (sys->lists[i].owner),
X    RESET (sys->lists[i].comment),
X    RESET (sys->lists[i].cmdoptions),
X    RESET (sys->lists[i].arch_dir),
X    RESET (sys->lists[i].farch_dir),
X    RESET (sys->lists[i].arch_pass),
X    RESET (sys->lists[i].arch_spec);
X  RESET (sys->server.address), RESET (sys->server.cmdoptions),
X  RESET (sys->server.comment), RESET (sys->serverd_cmdoptions),
X  RESET (sys->server.password), RESET (sys->arg), RESET (sys->manager),
X  RESET (sys->organization), RESET (sys->fax.prog);
X  sys->limits.msg = sys->limits.files = 100000;
X  sys->server.manager_prefs = 0;
X  strcpy (sys->server.address, DEFAULT_SERVER_ADDRESS);
X  strcpy (sys->server.cmdoptions, DEFAULT_SERVER_CMDOPTIONS);
X  strcpy (sys->server.comment, DEFAULT_SERVER_COMMENT);
X  strcpy (sys->manager, DEFAULT_MANAGER);
X  sys->mail.method = BINMAIL;
X  strcpy (sys->mail.precedence, DEFAULT_PRECEDENCE);
X  sys->options = 0;
X  sys->users = 100;
X  sys->frequency = 5;
X  sys->batch.start = 8; /* 8 am */
X  sys->batch.stop = 20; /* 8 pm */
X  OPEN_FILE (config, CONFIG, "r", "sys_config");
X  chmod (CONFIG, 384);
X  while (! feof (config)) {
X    line [0] = args [0] = RESET (cmd);
X    fgets (line, MAX_LINE - 2, config);
X    sscanf (line, "%s", cmd);
X    if (cmd[0] == EOS)
X      continue;
X/*    fgets (args, MAX_LINE - 2, config);*/
X    read_params (line, args, cmd, config, NULL);
X    if (args [strlen (args) - 1] == '\n')
X      args [strlen (args) - 1] = EOS;
X    if (cmd[0] == '#')
X      continue;
X    if (!strcmp (locase (cmd), "list")) {
X      if (nlists >= MAX_LISTS) {
X	report_progress (report,
X			 tsprintf ("\nsys_config(): List %s ignored: too many \
Xlists\n",
X				   args), FALSE);
X	continue;
X      }
X      comment = strchr (args, '#');
X      if (comment)
X	*comment = EOS;
X      sscanf (args, "%s %s %s %s", sys->lists [nlists].alias,
X	      sys->lists [nlists].address, sys->lists [nlists].owner,
X	      sys->lists [nlists].password);
X      if (get_list_id (sys->lists [nlists].alias, sys, nlists) >= 0 &&
X	  nlists > 0)
X	report_progress (report,
X			 tsprintf ("\nsys_config(): Duplicate list %s in %s",
X				   sys->lists [nlists].alias, CONFIG), TRUE),
X	exit (4);
X      cmdarg = _strstr (args, " -");
X      if (!cmdarg)
X	cmdarg = _strstr (args, "\t-");
X      if (cmdarg)
X	strcat (sys->lists [nlists].cmdoptions, cmdarg);
X      upcase (sys->lists [nlists].alias);
X      locase (sys->lists [nlists].address);
X      locase (sys->lists [nlists].owner);
X      upcase (sys->lists [nlists].password);
X      ++nlists;
X    }
X    else if (!strcmp (cmd, "server")) {
X      comment = strchr (args, '#');
X      if (comment)
X        *comment = EOS;
X      sscanf (args, "%s", sys->server.address);
X      cmdarg = _strstr (args, " -");
X      if (!cmdarg)
X	cmdarg = _strstr (args, "\t-");
X      if (cmdarg)
X	strcat (sys->server.cmdoptions, cmdarg);
X      locase (sys->server.address);
X    }
X    else if (!strcmp (cmd, "remote")) {
X      if ((remote = (REMOTE *) malloc (sizeof (*remote))) == NULL)
X	report_progress (report,
X			 tsprintf ("\nsys_config(): malloc() failed during \
X'remote' in %s",
X				   CONFIG), TRUE),
X	exit (11);
X      RESET (remote->inet_addr);
X      remote->port = 0;
X      sscanf (args, "%s %s %s %s %d", remote->alias, remote->address,
X	      remote->listserv, remote->inet_addr, &remote->port);
X      upcase (remote->alias);
X      locase (remote->address);
X      locase (remote->listserv);
X      if (remote->inet_addr[0] == '#')
X	RESET (remote->inet_addr);
X      else if (remote->port == 0)
X	remote->port = DEFAULT_IUL_PORT;
X      comment = strchr (args, '#');
X      if (!comment)
X        report_progress (report,
X			 tsprintf ("\nsys_config(): Missing # for 'remote' in %s",
X				   CONFIG), TRUE),
X        exit (4);
X      RESET (remote->comment);
X      strcat (remote->comment, comment + 1);
X      remote->next = rlists;  /* Link it to the existing list */
X      rlists = remote;
X    }
X    else if (!strcmp (cmd, "frequency")) {
X      sscanf (args, "%d", &sys->frequency);
X      if (sys->frequency < 0 || sys->frequency > 86400)
X	report_progress (report,
X			 tsprintf ("\nsys_config(): Invalid 'frequency' \
Xargument %d in %s",
X				   sys->frequency, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "limit")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "message"))
X	sys->options |= LIMIT_MSG,
X        sscanf (args, "%s %ld", sys->arg, &sys->limits.msg);
X      else if (!strcmp (sys->arg, "files"))
X	sys->options |= LIMIT_FILES,
X	sscanf (args, "%s %ld", sys->arg, &sys->limits.files);
X      else
X 	report_progress (report,
X			 tsprintf ("\nsys_config(): Invalid argument %s to \
X'limit' in %s",
X				   sys->arg, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "ceiling")) {
X      sscanf (args, "%s ", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X        report_progress (report,
X			 tsprintf ("\nsys_config(): Unrecognized list name %s \
Xfor 'ceiling' in %s", sys->arg, CONFIG), TRUE),
X        exit (4);
X      sscanf (args, "%s %d", sys->arg, &sys->lists[id].max_messages);
X    }
X    else if (!strcmp (cmd, "batch")) {
X      sscanf (args, "%u %u", &sys->batch.start, &sys->batch.stop);
X      sys->batch.start %= 24;
X      sys->batch.stop %= 24;
X      if (sys->batch.stop == 0)
X	sys->batch.stop = 24;
X      if (sys->batch.start >= sys->batch.stop ||
X	  (sys->batch.start == 0 && sys->batch.stop == 24))
X	report_progress (report, tsprintf ("\nsys_config(): Invalid times to \
X'batch' in %s",
X					   CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "option")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "bsd_ps"))
X	sys->options |= BSD_PS;
X      else if (!strcmp (sys->arg, "sysv_ps"))
X	sys->options |= SYSV_PS;
X      else if (!strcmp (sys->arg, "bsd_mail"))
X	sys->options |= BSD_MAIL;
X      else if (!strcmp (sys->arg, "bad_telnet"))
X	sys->options |= USE_MY_SYSTEM;
X      else if (!strcmp (sys->arg, "post_mail"))
X	sys->options |= POST_MAIL;
X      else if (!strcmp (sys->arg, "gate_mail"))
X	sys->options |= GATE_MAIL;
X      else if (!strcmp (sys->arg, "ignore_invalid_requests"))
X	sys->options |= IGNR_INVLD_RQSTS;
X      else if (!strcmp (sys->arg, "relaxed_syntax"))
X	sys->options |= RELAXED_SYNTAX;
X      else
X	report_progress (report, tsprintf ("\nsys_config(): Invalid argument \
X%s to 'option' in %s",
X					   sys->arg, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "serverd")) {
X      comment = strchr (args, '#');
X      if (comment)
X        *comment = EOS;
X      cmdarg = strchr (args, '-');
X      if (cmdarg)
X	strcat (sys->serverd_cmdoptions, cmdarg);
X    }
X    else if (!strcmp (cmd, "organization")) {
X      comment = strchr (args, '#');
X      if (comment)
X        *comment = EOS;
X      strcpy (sys->organization, args + 1);
X    }
X    else if (!strcmp (cmd, "restriction"))
X      sscanf (args, "%d", &sys->users);
X    else if (!strcmp (cmd, "manager"))
X      sscanf (args, "%s", sys->manager);
X    else if (!strcmp (cmd, "password"))
X      sscanf (args, "%s", sys->server.password),
X      upcase (sys->server.password);
X    else if (!strcmp (cmd, "disable")) {
X      if (commands[0].name == NULL)  /* Array not initialized */
X	continue;
X      sscanf (args, "%s", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X	  report_progress (report, tsprintf ("\nsys_config(): Unrecognized \
Xlist name %s for 'disable' in %s", sys->arg, CONFIG), TRUE),
X	  exit (4);
X      sscanf (args, "%s %s", sys->arg, sys->arg); /* Get request to disable */
X      upcase (sys->arg);
X      notok = FALSE;
X      k = 0;
X      for (i = 0; i < MAX_COMMANDS; ++i) {
X	notok &= (((j = strncmp (sys->arg, commands[i].name, strlen (sys->arg)))
X		   != 0) ? 1 : 0);
X	if (!j)
X	  ++k,
X	  sys->lists[id].disabled_commands |= commands[i].mask;
X      }
X      if (notok)
X	report_progress (report,
X			 tsprintf ("\nsys_config(): Unrecognized \
Xrequest %s to 'disable' for list %s in %s",
X				   sys->arg, sys->lists[id].alias, CONFIG),
X			 TRUE),
X	exit (4);
X      if (k > 1)
X	report_progress (report, tsprintf ("\nsys_config(): Ambiguous request \
X%s to 'disable' for list %s in %s", sys->arg, sys->lists[id].alias, CONFIG),
X			 TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "comment")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "server")) {
X	comment = strchr (args, '#');
X	if (!comment)
X          report_progress (report, tsprintf ("\nsys_config(): Missing # for \
X'comment' in %s",
X					     CONFIG), TRUE),
X          exit (4);
X	RESET (sys->server.comment);
X	strcat (sys->server.comment, comment + 1);
X      }
X      else {  /* Some list name was specified */
X	upcase (sys->arg);
X	id = get_list_id (sys->arg, sys, nlists);
X	if (id < 0)
X	  report_progress (report, tsprintf ("\nsys_config(): Unrecognized \
Xlist name %s for 'comment' in %s", sys->arg, CONFIG), TRUE),
X	  exit (4);
X	comment = strchr (args, '#');
X	if (!comment)
X	  report_progress (report, tsprintf ("\nsys_config(): Missing # for \
Xcomment in %s", CONFIG), TRUE),
X	  exit (4);
X	RESET (sys->lists[id].comment);
X	strcat (sys->lists[id].comment, comment + 1);
X      }
X    }
X    else if (!strcmp (cmd, "mailmethod")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "telnet"))
X	sys->mail.method = TELNET,
X 	sys->options |= USE_TELNET;
X      else if (!strcmp (locase (sys->arg), "system")) {
X#ifndef TCP_IP
X	report_progress (report, "\nsys_config(): No system support for \
X'system' mailmethod. Select another method.", TRUE);
X	exit (1);
X#endif
X	sys->options |= (USE_TELNET | USE_SYSMAIL);
X      }
X      else if (!strcmp (sys->arg, "sendmail") || !strcmp (sys->arg, "rmail")) {
X	if (!(sys->mail.method = (char *) malloc (256 * sizeof (char))))
X	  report_progress (report, tsprintf ("\nsys_config(): malloc() failed \
Xduring 'mailmethod' in %s", CONFIG), TRUE),
X	  exit (11);
X	sscanf (args, "%s %s", sys->arg, sys->mail.method);
X	locase (sys->mail.method);
X	strcat (sys->mail.method, " > /dev/null 2>&1");
X      }
X      else if (!strcmp (sys->arg, "binmail"))
X	sys->mail.method = BINMAIL;
X      else if (!strcmp (sys->arg, "env_var"))
X	sys->options |= USE_ENV_VAR,
X	sscanf (args, "%s %s %s", sys->arg, sys->mail.env_var,
X	        sys->mail.mail_prog),
X	locase (sys->mail.mail_prog),
X	upcase (sys->mail.env_var);
X      else
X	report_progress (report, tsprintf ("\nsys_config(): Unrecognized mail \
Xmethod %s in %s",
X					   sys->arg, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "digest")) { /* "digest list lines hours" */
X      sscanf (args, "%s ", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X        report_progress (report, tsprintf ("\nsys_config(): Unrecognized list \
Xname %s for 'digest' in %s", sys->arg, CONFIG), TRUE),
X        exit (4);
X      sscanf (args, "%s %d %d", sys->arg, &sys->lists[id].digest_lines,
X	      &sys->lists[id].digest_hours);
X    }
X    else if (!strcmp (cmd, "header")) { /* Precious header lines to be saved */
X      sscanf (args, "%s ", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X        report_progress (report, tsprintf ("\nsys_config(): Unrecognized list \
Xname %s for 'header' in %s", sys->arg, CONFIG), TRUE),
X        exit (4);
X      if (!(comment = strchr (args, '{')))
X	report_progress (report, tsprintf ("\nsys_config(): Missing '{' for \
X'header' in %s",
X					   CONFIG), TRUE),
X        exit (4);
X      RESET (sys->arg);
X      sys->lists[id].header = NULL;
X      while (!feof (config) && strcmp (sys->arg, "}")) {
X	RESET (sys->arg);
X	fscanf (config, "%s\n", sys->arg);
X	if (sys->arg[0] == '#') {
X	  fgets (sys->arg, MAX_LINE - 2, config);
X	  continue;
X	}
X	if (sys->arg[0] != EOS && strcmp (sys->arg, "}")) {
X	  if (! (header =
X		 (PRECIOUS_HEADER *) malloc (sizeof (PRECIOUS_HEADER))))
X	    report_progress (report, tsprintf ("\nsys_config(): malloc() \
Xfailed during 'header' in %s", CONFIG), TRUE),
X	    exit (11);
X	  if (! (header->line =
X	      (char *) malloc ((strlen (sys->arg) + 1) * sizeof (char))))
X	    report_progress (report, tsprintf ("\nsys_config(): malloc() \
Xfailed during 'header' in %s", CONFIG), TRUE),
X	    exit (11);
X	  strcpy (header->line, sys->arg);
X	  header->next = sys->lists[id].header;
X	  sys->lists[id].header = header;
X	}
X      }
X      if (feof (config) && strcmp (sys->arg, "}"))
X	report_progress (report, tsprintf ("\nsys_config(): Missing '}' for \
X'header' for list %s in %s", sys->lists[id].alias, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "default")) { /* Default list values */
X      sscanf (args, "%s ", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X	report_progress (report, tsprintf ("\nsys_config(): Unrecognized list \
Xname %s for 'default' in %s", sys->arg, CONFIG), TRUE),
X	exit (4);
X      if (!(comment = strchr (args, '{')))
X	report_progress (report, tsprintf ("\nsys_config(): Missing '{' for \
X'default' in %s",
X					   CONFIG), TRUE),
X	exit (4);
X      RESET (sys->arg);
X      while (!feof (config) && strcmp (sys->arg, "}")) {
X	RESET (sys->arg);
X	fscanf (config, "%s ", sys->arg);
X	upcase (sys->arg);
X	if (sys->arg[0] == '#') {
X	  fgets (sys->arg, MAX_LINE - 2, config);
X	  continue;
X	}
X	if (sys->arg[0] != EOS && strcmp (sys->arg, "}")) {
X	  found = FALSE;
X	  for (i = 0; i < MAX_SET_OPTIONS; i++)
X	    if (!strcmp (sys->arg, options[i])) {
X	      found = TRUE;
X	      break;
X	    }
X	  if (!found)
X	    report_progress (report, tsprintf ("\nsys_config(): Default value \
X%s not recognized in %s", sys->arg, CONFIG), TRUE),
X	    exit (4);
X	  fscanf (config, "%s %s\n", line, line);
X	  if (!re_strcmp (values[i], upcase (line), NULL))
X	    report_progress (report, tsprintf ("\nsys_config(): %s not a valid \
Xdefault value for %s in %s", line, options[i], CONFIG), TRUE),
X	    exit (4);
X	  strcpy (sys->lists[id].defaults.set_values[i], line);
X	}
X      }
X      if (feof (config) && strcmp (sys->arg, "}"))
X	report_progress (report, tsprintf ("\nsys_config(): Missing '}' for \
X'default'  for list %s in %s", sys->lists[id].alias, CONFIG), TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "precedence"))
X      sscanf (args, "%s", sys->mail.precedence);
X    else if (!strcmp (cmd, "fax"))
X      strcpy (sys->fax.prog, args);
X    else if (!strcmp (cmd, "archive")) {
X      /* USER CONTRIBUTED CODE: Warren Burstein
X         "archive list dir spec [farch-dir] [password] [digest]"
X	 
X         dir must be an absolute path
X       
X         farch-dir should be relative to HOMEDIR/archives
X         so we can create all the INDEX files
X       
X         if you don't want digests but do want farch-dir, set the field
X         to anything but digest, if you don't want farch-dir just omit it.
X      */
X      char digest [MAX_LINE];
X
X      RESET (digest);
X      sscanf (args, "%s ", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X        report_progress (report, tsprintf ("\nsys_config(): Unrecognized list \
Xname %s for 'archive' in %s", sys->arg, CONFIG), TRUE),
X        exit (4);
X      sys->lists[id].options |= ARCHIVE_LIST;
X      sscanf (args, "%s %s %s %s %s %s",
X              sys->arg, sys->lists[id].arch_dir, sys->lists[id].arch_spec,
X              sys->lists[id].farch_dir, sys->lists[id].arch_pass, digest);
X
X      locase (sys->lists[id].arch_spec);
X      if (*sys->lists[id].arch_dir != '/') {
X        report_progress (report, tsprintf ("\nsys_config(): archive-dir for %s \
Xdoesn't start with /",
X					   sys->arg, CONFIG), TRUE),
X        exit (4);
X      }
X
X      if (*sys->lists[id].farch_dir == '/') {
X        report_progress (report, tsprintf ("\nsys_config(): farch-dir for %s \
Xstarts with /",
X					   sys->arg, CONFIG), TRUE),
X        exit (4);
X      }
X
X      if (*sys->lists[id].farch_dir == '-' || *sys->lists[id].farch_dir == EOS)
X	sprintf (sys->lists[id].farch_dir, DEFAULT_ARCHIVE);
X
X      if (!strcmp (sys->lists[id].arch_pass, "-"))
X	RESET (sys->lists[id].arch_pass);
X
X      if (!strcmp (locase (digest), "digest"))
X	sys->lists[id].options |= ARCHIVE_DIGEST;
X    }
X    else if (!strcmp (cmd, "unix_cmd")) {
X      sscanf (args, "%s", sys->arg);
X      upcase (sys->arg);
X      id = get_list_id (sys->arg, sys, nlists);
X      if (id < 0)
X	report_progress (report, tsprintf ("\nsys_config(): Unrecognized list \
Xname %s for 'unix_cmd' in %s", sys->arg, CONFIG), TRUE),
X	exit (4);
X      RESET (arg);
X      sscanf (args, "%s %s", sys->arg, arg);
X      if (arg[0] == EOS || arg[0] == '#' || arg[0] == '\'')
X	report_progress (report, tsprintf ("\nsys_config(): Missing or invalid \
Xpassword %s for 'unix_cmd' in %s", arg, CONFIG), TRUE),
X	exit (4);
X      if ((unix_cmd = (UNIX_CMDS *) malloc (sizeof (*unix_cmd))) == NULL)
X	report_progress (report, tsprintf ("\nsys_config(): malloc() failed \
Xduring 'unix_cmd' in %s", CONFIG), TRUE),
X	exit (11);
X      strcpy (unix_cmd->password, arg);
X      upcase (unix_cmd->password);
X      RESET (arg);
X      sscanf (args, "%s %s %s", sys->arg, sys->arg, arg);
X      if (arg[0] == EOS || arg[0] == '#' || arg[0] == '\'')
X	report_progress (report, tsprintf ("\nsys_config(): Missing or invalid \
Xcommand name %s for 'unix_cmd' in %s", arg, CONFIG), TRUE),
X	exit (4);
X      strcpy (unix_cmd->name, arg);
X      upcase (unix_cmd->name);
X      start = strchr (args, '\'');
X      end = strrchr (args, '\'');
X      if (!start || !end || start == end)
X	report_progress (report, tsprintf ("\nsys_config(): Missing ''' for \
X'unix_cmd' in %s",
X					   CONFIG), TRUE),
X	exit (4);
X      if (! (unix_cmd->cmd =
X	  (char *) malloc ((abs (end - start - 1) + 1) * sizeof (char))))
X	report_progress (report, tsprintf ("\nsys_config(): malloc() failed \
Xduring 'unix_cmd' in %s", CONFIG), TRUE),
X	exit (11);
X      strncpy (unix_cmd->cmd, start + 1, abs (end - start - 1));
X      unix_cmd->cmd[abs (end - start - 1)] = EOS;
X      if (!(comment = strchr (args, '#')))
X	report_progress (report, tsprintf ("\nsys_config(): Missing '#' for \
X'unix_cmd' in %s",
X					   CONFIG), TRUE),
X	exit (4);
X      if (! (unix_cmd->comment =
X	  (char *) malloc ((strlen (comment + 1) + 1) * sizeof (char))))
X	report_progress (report, tsprintf ("\nsys_config(): malloc() failed \
Xduring 'unix_cmd' in %s", CONFIG), TRUE),
X	exit (11);
X      strcpy (unix_cmd->comment, comment + 1);
X      unix_cmd->next = sys->lists[id].unix_cmds;
X      sys->lists[id].unix_cmds = unix_cmd;
X    }
X    else
X      report_progress (report, tsprintf ("\nsys_config(): Unrecognized \
Xdirective %s in %s", cmd, CONFIG), TRUE),
X      exit (4);
X  }
X  fclose (config);
X  config_owner_prefs (sys, nlists, report);
X  return nlists;
X}
X
X/*
X  Get owner and manager preferences from OWNERSF.
X
X  Enhanced by: Warren Burstein.
X*/
X
Xvoid config_owner_prefs (SYS *sys, int nlists, FILE *report)
X{
X  FILE *f;
X  char registered_owner [MAX_LINE];
X  char assigned_list [MAX_LINE];
X  char pref [MAX_OWNER_PREFS] [MAX_LINE];
X  char prefs [MAX_LINE];
X  int listid, i, *op;
X
X  OPEN_FILE (f, OWNERSF, "r", "config_owner_prefs");
X  while (!feof (f)) {
X    assigned_list[0] = RESET (prefs);
X    fscanf (f, "%s %s", registered_owner, assigned_list);
X    upcase (assigned_list);
X    fgets (prefs, MAX_LINE - 2, f);
X    if (assigned_list [0] != EOS && registered_owner[0] != '#') {
X      for (i = 0; i < MAX_OWNER_PREFS; i++)
X	RESET (pref [i]);
X      sscanf (prefs, "%s %s %s %s %s %s %s %s", pref [0], pref [1], pref [2],
X	      pref [3], pref [4], pref [5], pref [6], pref [7]);
X      if (!strcmp (assigned_list, "SERVER"))
X	op = &(sys->server.manager_prefs);
X      else if ((listid = get_list_id (assigned_list, sys, nlists)) >= 0)
X	op = &(sys->lists[listid].owner_prefs);
X      else
X	report_progress (report, tsprintf ("\nconfig_owner_prefs(): Unknown \
Xlist %s in %s",
X					   assigned_list, OWNERSF), TRUE),
X	exit (4);
X      for (i = 0; i < MAX_OWNER_PREFS && pref [i][0] != EOS; i++) {
X	/* Undocumented: also support -CCxxx systax: keni@oasys.dt.navy.mil */
X	char *pi = pref[i];
X	int pil = strlen (pi);
X	int neg = 0;
X	if (pi[0] == '-')
X	  neg = 1,
X	  --pil,
X	  ++pi,
X	  *op = ~*op;
X	if (!strncmp (pi, CCALL, pil))
X	  *op |= ccall;
X	else if (!strncmp (pi, CCERRORS, pil))
X	  *op |= ccerrors;
X	else if (!strncmp (pi, CCSUB, pil))
X	  *op |= ccsub;
X	else if (!strncmp (pi, CCUNSUB, pil))
X	  *op |= ccunsub;
X	else if (!strncmp (pi, CCSET, pil))
X	  *op |= ccset;
X	else if (!strncmp (pi, CCREC, pil))
X	  *op |= ccrec;
X	else if (!strncmp (pi, CCINFO, pil))
X	  *op |= ccinfo;
X	else if (!strncmp (pi, CCSTAT, pil))
X	  *op |= ccstat;
X	else if (!strncmp (pi, CCGET, pil))
X	  *op |= ccget;
X	else if (!strncmp (pi, CCINDEX, pil))
X	  *op |= ccindex;
X	else if (!strncmp (pi, CCLISTS, pil))
X	  *op |= cclists;
X	else if (!strncmp (pi, CCRELEASE, pil))
X	  *op |= ccrelease;
X	else if (!strncmp (pi, CCHELP, pil))
X	  *op |= cchelp;
X	else if (!strncmp (pi, CCPRIVATE, pil))
X	  *op |= ccprivate;
X	else if (!strncmp (pi, CCRUN, pil))
X	  *op |= ccrun;
X	else if (pref [i][0] != EOS)
X	  report_progress (report, tsprintf ("\nconfig_owner_prefs(): Unknown \
Xpreference %s in %s",
X					     pref [i], OWNERSF), TRUE),
X	  exit (4);
X	if (neg)
X	  *op = ~*op;
X      }
X    }
X  }
X  fclose (f);
X}
X
X/*
X  Convert a string to lower case.
X*/
X
Xchar *locase (char *s)
X{
X  char *r = s;
X  while (*s != EOS) {
X    if (isupper (*s))
X      *s = (char) tolower (*s);
X    ++s;
X  }
X  return r;
X}
X
X/*
X  Convert a string to upper case.
X*/
X
Xchar *upcase (char *s)
X{
X  char *r = s;
X
X  while (*s != EOS) {
X    if (islower (*s))
X      *s = (char) toupper (*s);
X    ++s;
X  }
X  return r;
X}
X
X/*
X  Replace all characters of the first word in 's' with "X".
X*/
X
Xvoid shadow_password (char *s)
X{
X  while (*s != EOS && isspace (*s)) /* Get to first word */
X    ++s;
X  if (*s != EOS)
X    while (!isspace (*s))
X      *s = 'X',
X      ++s;
X}
X
X/*
X  Write messages and times to 'report' and stdout. If 'report_time'
X  is set to a negative value no leading newline is printed to 'report'.
X  NOTE: Use strftime(), if possible.
X*/
X
Xvoid report_progress (FILE *report, char *s, int report_time)
X{
X#ifdef SYSLOG
X  char *buf;
X  int i;
X
X  if (!(buf = (char *) malloc ((strlen (s) + 1) * sizeof (char))))
X    exit (11);
X  strcpy (buf, s);
X  for (i = 0; i < strlen (buf); i++)
X    if (buf[i] == '\n')
X      buf[i] = ' ';
X  syslog (LOG_INFO, buf);
X  free ((char *) buf);
X#else
X  extern BOOLEAN tty_echo;
X  long int time_is = 0;
X  struct tm *t;
X
X  if (report) {
X    fprintf (report, "%s", s);
X    if (report_time) {
X      time (&time_is);
X      t = localtime (&time_is);
X      if (report_time > 0)
X	fprintf (report, "\n");
X      fprintf (report, "Time/Date: %2d:%.2d:%.2d, %2d/%.2d/%2d\n",
X	       t->tm_hour, t->tm_min, t->tm_sec, t->tm_mon + 1, t->tm_mday,
X	       t->tm_year);
X    }
X    fflush (report);
X  }
X  if (tty_echo) {
X    printf ("%s", s);
X    if (report_time)
X      printf ("\n");
X    fflush (stdout);
X  }
X#endif
X}
X
X/*
X  Start distribution. Call lower level routines; the algorithm is such that
X  this routine always looks at the beginning of each message when reading
X  into 'first_line', i.e. 'first_line' is always assigned a string of the form:
X		'From emailaddress Date Time'
X  which is the universal convention for the start of every message. It calls
X  subscribed() to find out if the sender is subscribed and passes the
X  result to process_message(). Since the call to extract_sender() alters
X  'first_line' to contain only the sender's email address a 'linecopy' is used.
X  When returning from process_message() we have already advanced to the
X  beginning of the next message, and 'linecopy' contains a string of the 
X  above form; therefore we need to copy that back to 'first_line'.
X*/
X
Xvoid distribute (FILE *mail, void (*process_message)(), FILE *report,
X		 char *Subscribersf, char *Newsf, char *Peersf, char *Aliasesf)
X{
X  char first_line[MAX_LINE], linecopy[MAX_LINE];
X  BOOLEAN address_status;
X
X  first_line[0] = RESET (linecopy);
X  while (!feof (mail))
X    if (first_line[0] != EOS)
X      address_status = extract_sender (first_line),
X      process_message ((char *) first_line, (char *) linecopy,
X		       (BOOLEAN) address_status,
X		       (BOOLEAN)
X		        subscribed (report, first_line, Subscribersf, Newsf,
X				    Peersf, Aliasesf)),
X      strcpy (first_line, linecopy);
X    else /* Read the first line of the very first message */
X      fgets (first_line, MAX_LINE - 2, mail), /* 'From email Date Time' */
X      strcpy (linecopy, first_line);
X}
X
X/*
X  Look for occurence of 'string' in 'substr' and return either TRUE or FALSE.
X  Look at the comments for defs.h for the syntax of 'substr'. A 'substr' of
X  ".*" matches everything.
X*/
X
XBOOLEAN strinstr (char *substr, char *string)
X{
X  char *substrcopy, *stringcopy;
X  extern FILE *report;
X
X  substrcopy = (char *) malloc ((strlen (substr) + 1) * sizeof (char));
X  stringcopy = (char *) malloc ((strlen (string) + 1) * sizeof (char));
X  if (!substrcopy || !stringcopy)
X    report_progress (report, "\nstrinstr(): malloc() failed", TRUE),
X    exit (11);
X  strcpy (substrcopy, substr);
X  strcpy (stringcopy, string);
X  upcase (substrcopy);
X  upcase (stringcopy);
X  if (re_strcmp (substrcopy, stringcopy, NULL) > 0) {
X    free ((char *) substrcopy);
X    free ((char *) stringcopy);
X    return TRUE;
X  }
X  free ((char *) stringcopy);
X  free ((char *) substrcopy);
X  return FALSE;
X}
X
X/*
X  Given a file path, extract the file name. In the process, any command line
X  options or any characters separated from the filename are discarded.
X*/
X
Xchar *extract_filename (char *s)
X{
X  char *p, *r, *t = s, nchar = 0;
X  extern FILE *report;
X
X  while (*t != EOS && *t != ' ' && *t != '\t') /* Get to delimiting char */
X    ++t;
X  while (t != s && *t != '/') /* Go back till / or the beginning of s */
X    ++nchar,
X    --t;
X  if (t != s || (*t == '/' && *(t + 1) != EOS))
X    --nchar,
X    ++t;
X  if (! (r = p = (char *) malloc ((nchar + 1) * sizeof (char))))
X    report_progress (report, "\nextract_filename(): malloc() failed", TRUE),
X    exit (11);
X  *p = EOS;
X  while (*t != EOS && *t != ' ' && *t != '\t')
X    *(p++) = *(t++);
X  *p = EOS;
X  return r;
X}
X
X/*
X  Recognize the command line parameters. It returns '?' on an unrecognized
X  option, ':' if an option requires an argument and the argument is missing,
X  or the recognized character itself. This code is a copyright of AT&T.
X*/
X
X# define ERR(str, ch) if (opterr) \
X                        fprintf (stderr, "%s%s%c\n", argv[0], str, ch);
X
Xint _getopt (int argc, char **argv, char *opts)
X{
X  static int sp = 1;
X  register int c;
X  register char *cp;
X  extern int opterr, optind, optopt;
X  extern char *optarg;
X
X  if (sp == 1)
X    if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
X      return EOF;
X    else if (!strcmp (argv[optind], "--")) {
X      optind++;
X      return EOF;
X    }
X    optopt = c = argv[optind][sp];
X    if (c == ':' || (cp = strchr (opts, c)) == NULL) {
X      ERR (": unknown option, -", c);
X      if (argv[optind][++sp] == '\0')
X        optind++,
X        sp = 1;
X      return '?';
X    }
X    if (*++cp == ':') {
X      if (argv[optind][sp+1] != '\0')
X        optarg = &argv[optind++][sp+1];
X      else if (++optind >= argc) {
X        ERR(": argument missing for -", c);
X        sp = 1;
X        return ':';
X      }
X      else
X        optarg = argv[optind++];
X      sp = 1;
X    }
X    else {
X      if (argv[optind][++sp] == '\0')
X        sp = 1,
X        optind++;
X      optarg = NULL;
X    }
X    return c;
X}
X
X/*
X  Remove any extraneous characters from a name and convert it to lower
X  case with the first character of each word capitalized [currently disbled].
X*/
X
Xchar *clean_name (char *s)
X{
X  char *tok, *copy, *sep = " ", *line;
X  int j, l;
X  extern FILE *report;
X
X  if (s [0] != EOS && s [strlen (s) - 1] == '\n')
X    s [strlen (s) - 1] = EOS;
X  if (! (line = (char *) malloc ((strlen (s) + 1) * sizeof (char))))
X    report_progress (report, "\nclean_name(): malloc() failed", TRUE),
X    exit (11);
X  strcpy (line, s);
X  *s = EOS;
X  tok = strtok (line, sep);
X  while (tok) {
X    copy = tok;
X    while (*copy != EOS) {  /* Remove extraneous characters */
X      if (! isalpha (*copy) && ! isspace (*copy) && ! isdigit (*copy) &&
X	  *copy != '-' && *copy != '.' && *copy != '@') {
X        for (j = 0, l = strlen (copy); j < l; copy [j] = copy [j + 1], ++j);
X        continue;
X      }
X      ++copy;
X    }
X    locase (tok);
X    *tok = (char) toupper (*tok);
X    sprintf (s + strlen (s), " %s", tok);
X    tok = strtok (NULL, sep);
X  }
X  free ((char *) line);
X  return s;
X}
X
X/*
X  Remove leading blanks and extraneous characters from a request.
X*/
X
Xvoid clean_request (char *s)
X{
X  while (isspace (*s))
X    sprintf (s, "%s", s + 1);
X  while (*s != EOS) {
X    if (*s < ' ' && *s != '\t' && *s != '\n' && *s != '\r')
X      *s = EOS;
X    ++s;
X  }
X}
X
X/*
X  Identify the list that the request refers to. Before doing so, remove
X  any unnecessary blanks from the parameters. Also, check each parameter
X  for proper syntax.
X*/
X
Xvoid get_list_name (char *params, char *list_name)
X{
X  int i;
X  char *s, *r, *t;
X  char param [MAX_LINE];
X  extern FILE *report;
X
X  if (! (r = s = (char *) malloc ((strlen (params) + 1) * sizeof (char))))
X    report_progress (report, "\nget_list_name(): malloc() failed", TRUE),
X    exit (11);
X  strcpy (s, params);
X  param [0] = RESET (params);
X  for (i = 0; s[i] != EOS;)
X    if (isspace (s[i]) && isspace (s[i + 1]))
X      sprintf (s + i, "%s", s + i + 1);
X    else
X      i++;
X  do {
X    RESET (param);
X    sscanf (s, "%s", param);
X    s = s + strlen (param) + 1; /* Skip over space */
X    t = strpbrk (param, "*?/`");
X    if (t != NULL) {
X      if (t == param || (t != param && ! isalpha (*(t - 1)))) /* Invalid *,? */
X	*t = '#';
X      if (*t == '/') /* Invalid / */
X	*t = '%';
X      if (*t == '`') /* Invalid ` */
X	*t = '-';
X    }
X    sprintf (params + strlen (params), " %s", param);
X  } while (param[0] != EOS);
X  strcat (params, "\n");
X  free ((char *) r);
X  RESET (list_name);
X  sscanf (params, "%s", list_name);
X  upcase (list_name);
X  sprintf (params, "%s", params + strlen (list_name) + 1);
X}
X
X/*
X  Free the current list of matched remote lists.
X*/
X
Xvoid free_remote_matched (REMOTE **matched_rlists)
X{
X  REMOTE *next;
X
X  while (*matched_rlists)  /* Free old list */
X    next = (*matched_rlists)->next,
X    free ((REMOTE *) *matched_rlists),
X    *matched_rlists = next;
X}
X
X/*
X  Given a list name, return its index in the array of known lists. If the
X  list is a remote list, return nlists to signify this effect.
X*/
X
Xint get_list_id (char *list_name, SYS *sys, int nlists)
X{
X  int i;
X  char *p, address [MAX_LINE];;
X
X  free_remote_matched (&matched_rlists);
X  for (i = 0; i < nlists; i++) {
X    if (!strcmp (list_name, sys->lists[i].alias))
X      return i;
X    strcpy (address, sys->lists[i].address);
X    upcase (address);
X    if (!strcmp (list_name, address) && (p = strchr (list_name, '@'))) {
X      *p = EOS;
X      return i;
X    }
X  }
X  if (check_remote (rlists, list_name)) {
X    /* Store list name above all known lists */
X    strcpy (sys->lists[nlists].alias, list_name);
X    return nlists;
X  }
X  return -1;
X}
X
X/*
X  Check whether 'list_name' is a remote list. Return a linked list
X  of matched records, or NULL. The list is stored in the global variable
X  'matched_rlists'.
X*/
X
XREMOTE *check_remote (REMOTE *rlists, char *list_name)
X{
X  REMOTE *new;
X  char address [MAX_LINE];
X  extern FILE *report;
X
X  while (rlists) {
X    strcpy (address, rlists->address);
X    upcase (address);
X    if (!strcmp (list_name, rlists->alias) ||
X	!strcmp (list_name, address)) {
X      if ((new = (REMOTE *) malloc (sizeof (*new))) == NULL)
X	report_progress (report, "\ncheck_remote(): malloc() failed", TRUE),
X	exit (11);
X      memcpy ((char *) new, (char *) rlists, sizeof (*new));
X      new->next = matched_rlists;
X      matched_rlists = new;
X    }
X    rlists = rlists->next;
X  }
X  return matched_rlists;
X}
X
Xvoid setup_string (char *s, char *alias, char *filename)
X{
X  RESET (s);
X  SETUP_STRING;
X}
X
X/*
X  Return the first occurence of 'sub' in 'src', or NULL if not found.
X*/
X
Xchar *_strstr (char *src, char *sub)
X{
X  if (src == NULL || sub == NULL)
X    return NULL;
X  while (*src != EOS) {
X    if (!strncmp (src, sub, strlen (sub)))
X      return src;
X    ++src;
X  }
X  return NULL;
X}
X
X/*
X  Shrink a file so that it contains a maximum of MAX_FILE_LENGTH entries in it.
X*/
X
Xvoid shrink (char *s)
X{
X  struct stat buf;
X  char *tmpmsg;
X
X  if (s[0] == EOS || stat (s, &buf))
X    return;
X  syscom ("tail -%d %s > %s", MAX_FILE_LENGTH, s,
X	  (tmpmsg = mystrdup (tmpnam (NULL))));
X  mv (tmpmsg, s);
X  free ((char *) tmpmsg);
X}
X
X/*
X  Verify that part 'i' (of a file split into different parts by farch)
X  is in 's'.
X*/
X
XBOOLEAN requested_part (char *s, int i)
X{
X  char buf [80];
X  char copy [MAX_LINE];
X
X  strncpy (copy, s, MAX_LINE - 1);
X  copy [MAX_LINE - 1] = EOS;
X  do {
X    RESET (buf);
X    sscanf (copy, "%s", buf);
X    sprintf (copy, "%s", strchr (copy, buf[0]) + strlen (buf));
X    if (atoi (buf) == i)
X      return TRUE;
X  } while (buf[0] != EOS);
X  return FALSE;
X}
X
X/*
X  Free the REMOTE linked list.
X*/
X
Xvoid free_remote (REMOTE **rlists)
X{
X  REMOTE *next;
X
X  while (*rlists)
X    next = (*rlists)->next,
X    free ((REMOTE *) *rlists),
X    *rlists = next;
X  *rlists = NULL;
X}
X
X/*
X  Use my_system() when either list and listserv use TELNET and there is
X  an indication that the sessions do not exit.
X*/
X
Xint my_system (char *s)
X{
X  FILE *f;
X  int pid, status;
X  char line [MAX_LINE], *r;
X  extern SYS sys;
X
X  if (strcmp ((r = extract_filename (s)), "telnet")) {
X    status = system (s);
X    free ((char *) r);
X    return status;
X  }
X  free ((char *) r);
X  strcat (s, " &"); /* Only telnet runs in the background */
X  status = system (s);
X  if (status > 127)
X    return status;
X  sleep (5);
X  if (sys.options & BSD_PS)
X    system ("ps -gx | grep telnet | grep -v \"grep telnet\" \
X	    | grep -v telnetd > ./telnet");
X  else if (sys.options & SYSV_PS)
X    system ("ps -ef | grep telnet | grep -v \"grep telnet\" \
X	    | grep -v telnetd > ./telnet");
X  if ((f = fopen ("./telnet", "r")) == NULL)
X    return -1;
X  while (! feof (f)) {
X    RESET (line);
X    fgets (line, MAX_LINE - 2, f);
X    if (line[0] != EOS)
X      sscanf (line, "%d", &pid),
X      sleep (15),
X      kill (pid, SIGHUP);
X  }
X  fclose (f);
X  unlink ("./telnet");
X  return status;
X}
X
X/* Change history (by Bob Boyd)
X21-Aug-1991   RLB     change parameter scanning for "list" lines in the
X                      config file so that "-" characters in the list-alias
X                      are ignored when looking for the beginning of the
X                      list parameters [tasos: I defined my own strstr()]
X*/
X
X/*
X  Remove the message identified as 'tag_to_remove' from the specified 'file'.
X  Return TRUE if successful, FALSE if the message was not located.
X*/
X
XBOOLEAN remove_msg (char *file, int tag_to_remove, FILE *report)
X{
X  FILE *in, *out;
X  char prev_line [MAX_LINE];
X  char line [MAX_LINE];
X  char match [MAX_LINE];
X  char *tmp_moderated_f;
X  BOOLEAN copy = FALSE, message_found = FALSE;
X  int tag;
X
X  mv (file, (tmp_moderated_f = mystrdup (tmpnam (NULL))));
X  chmod (tmp_moderated_f, 416); /* 640 */
X  OPEN_FILE (in, tmp_moderated_f, "r", "remove_msg");
X  OPEN_FILE (out, file, "w", "remove_msg");
X  prev_line[0] = RESET (line);
X  while (!feof (in)) {
X    if (!strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)) &&
X	!copy)
X      RESET (prev_line),
X      copy = TRUE;
X    strcpy (match, "\\1");
X    if (re_strcmp (MESSAGE_TAG, line, match) > 0) { /* Get tag # */
X      sprintf (line, "%s", line + strlen (match));
X      sscanf (line, "%d", &tag);
X      sprintf (line, "%s%d\n", match, tag);
X      if (tag_to_remove == tag)
X	message_found = TRUE,
X	copy = FALSE;
X    }
X    if (copy && prev_line[0] != EOS)
X      fprintf (out, "%s", prev_line);
X    strcpy (prev_line, line);
X    RESET (line);
X    fgets (line, MAX_LINE - 2, in);
X  }
X  if (copy && prev_line[0] != EOS)
X    fprintf (out, "%s", prev_line);
X  fclose (in);
X  fclose (out);
X  unlink (tmp_moderated_f);
X  free ((char *) tmp_moderated_f);
X  return message_found;
X}
X
X/*
X  Read the parameters from 'line' that follow 'request' in 'line',
X  and possibly from lines below in the 'mail' file, if 'line' ends
X  with &. Continuation lines should always end with '&' and no
X  trailing blanks or any other characters except new-line.
X  'params' should end with a \n.
X*/
X
Xvoid read_params (char *line, char *params, char *request, FILE *mail,
X		  FILE *report)
X{
X  char l [MAX_LINE];
X
X  sprintf (params, "%s", line + strlen (request));
X  if (params [strlen (params) - 1] == '\n')
X    params [strlen (params) - 1] = EOS; /* Remove trailing \n */
X  RESET (l);
X  while (!feof (mail) &&
X	 strncmp (l, START_OF_MESSAGE, strlen (START_OF_MESSAGE)) &&
X	 params [0] != EOS && params [strlen (params) - 1] == '&') {
X    params [strlen (params) - 1] = EOS; /* Replace & */
X    RESET (l);
X    fgets (l, MAX_LINE - 2, mail);
X    if (l [0] != EOS &&
X	strncmp (l, START_OF_MESSAGE, strlen (START_OF_MESSAGE))) {
X      if (report)
X	report_progress (report, l, FALSE);
X      l [strlen (l) - 1] = EOS, /* Remove trailing \n */
X      strcat (params, l);
X    }
X  }
X  strcat (params, "\n");
X  if (!strncmp (l, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))
X    fseek (mail, -strlen (l), SEEK_CUR); /* Move back to beginning of new msg */
X}
X
X/*
X  Open and lock file. It returns the opened file descriptor, or
X  CANT_OPEN or CANT_LOCK.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xint lock_file (char *file, int flag, int mode, BOOLEAN delay)
X{
X#ifdef NO_LOCKS
X  return 0;
X#else
X  int count, fd, lock;
X
X  if ((fd = open (file, flag, mode)) < 0)
X    return CANT_OPEN;
X  for (count = 0; (lock = lockf (fd, F_TLOCK, 0)) && (count < 180) && delay;
X       ++count, sleep (1));
X  if (lock) {
X    close (fd); 
X    return CANT_LOCK;
X  }
X  return fd;
X#endif
X}
X
X/*
X  Unlock and close the specified file descriptor.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
Xvoid unlock_file (int fd)
X{
X#ifndef NO_LOCKS
X  if (fd >= 0)
X    lockf (fd, F_ULOCK, 0),
X    close (fd);
X#endif
X}
X
X/*
X  Write to a non-blocking file descriptor.
X*/
X
Xlong int write_to_fd (int fd, char *buf, long int bytes_to_write)
X{
X  long int bytes_written, total_bytes = 0, bytes_requested = bytes_to_write;
X  extern FILE *report;
X
X  errno = 0;
X  while ((bytes_written = write (fd, buf, bytes_to_write)) < bytes_to_write) {
X    if (bytes_written < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN
X	&& errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      sprintf (error, "\nwrite_to_fd(): ");
X      switch (errno) {
X	case EBADF: strcat (error, "Bad file number"); break;
X	case EFAULT: strcat (error, "Bad address"); break;
X	case EFBIG: strcat (error, "File limit reached"); break;
X	case EINVAL: strcat (error, "Negative seek pointer"); break;
X	case EIO: strcat (error, "I/O error"); break;
X	case ENOSPC: strcat (error, "No space left on device"); break;
X	case ENXIO: strcat (error, "No such device or address"); break;
X	case ERANGE: sprintf (error + strlen (error), "Bytes to write (%d) \
Xout of range", bytes_to_write); break;
X	case EPIPE: sprintf (error, "Client disappeared"); break;
X	default: sprintf (error + strlen (error), "Error number %d", errno);
X      }
X      report_progress (report, error, TRUE);
X      if (bytes_written > 0)
X	total_bytes += bytes_written;
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    if (bytes_written > 0)
X      bytes_to_write -= bytes_written,
X      total_bytes += bytes_written,
X      buf += bytes_written;
X    errno = 0;
X  }
X  return bytes_requested;
X}
X
X/*
X  Improved mkdir - builds all parents of full_path if needed.
X  If a mkdir fails, or a component of the directory exists but
X  is not a directory, return FALSE and store a description in report_msg.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
XBOOLEAN mkdir1 (char *full_path, char *report_msg, char *mask)
X{
X  BOOLEAN first = TRUE;
X  char *p = full_path, d[MAX_LINE], dir[MAX_LINE], head[MAX_LINE];
X  struct stat st;
X
X  RESET (head);
X  while (get_field (&p, '/', d)) {
X    if (first) {
X      first = FALSE;
X
X      if (d[0] == EOS) {
X	strcpy (head, "/");
X	continue;
X      }
X    }
X
X    sprintf (dir, "%s%s", head, d);
X    sprintf (head, "%s/", dir);
X
X    if (stat (dir, &st)) {
X      if (mkdir (dir, 0755 & (0755 ^ otoi (mask)))) {
X	sprintf (report_msg, "mkdir1(%s): mkdir %s failed, errno %d\n",
X		 full_path, dir, errno);
X	return FALSE;
X      }
X    } 
X    else if ((st.st_mode & S_IFMT) != S_IFDIR) {
X      sprintf (report_msg, "mkdir1: %s is not a directory", dir);
X      return FALSE;
X    }
X  }
X  return TRUE;
X}
X
X/*
X  Convert an octal number stored in a string to integer.
X*/
X
Xint otoi (char *s)
X{
X  int i, n;
X
X  n = 0;
X  for (i = 0; s[i] >= '0' && s[i] <= '7'; ++i)
X    n = 8 * n + (s[i] - '0');
X  return n;
X}
X
X/*
X  Find a field in *src ending with "sep". Leave src pointing at char after sep,
X  store field in dst. If src points at end of string, return FALSE. If src
X  points at a separator, make dst a null string.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
XBOOLEAN get_field (char **src, char sep, char *dst)
X{
X  char *s = *src;
X  int len;
X
X  if (**src == EOS)
X    return FALSE;
X
X  while (**src != EOS && **src != sep)
X    ++*src;
X
X  if (*src == s) {
X    *dst = EOS;
X    if (**src != EOS)
X      ++*src;
X    return TRUE;
X  }
X
X  len = *src - s;
X  (void) strncpy (dst, s, len);
X  dst[len] = EOS;
X
X  if (**src != EOS)                  /* advance past separator */
X    ++*src;
X
X  return TRUE;
X}
X
X/*
X  Make the directory for dirf (a DIR file) and create/update all
X  necessary INDEX files.
X
X  USER CONTRIBUTED FUNCTION: Warren Burstein
X*/
X
XBOOLEAN make_indexes (char *dirf, char *farch_dir, char *password, char *error,
X		      char *mask)
X{
X  char dirname [MAX_LINE], *p, *q;
X  struct dirmatch {
X    char *s;
X    BOOLEAN match;
X  } *dirs;
X  int n_dirs, i, j;
X  extern FILE *report;
X
X  /* Get the directory that INDEX/DIR is in and make it if needed. */
X  strcpy (dirname, dirf);
X  if (p = strrchr (dirname, '/'))
X   *p = EOS;
X  if (!mkdir1 (dirname, error, mask))
X    return FALSE;
X
X  /* The number of dirs is one higher than the number of slashes. Add one
X     more for the top level directory, ARCHIVE_DIR */
X  for (p = farch_dir, n_dirs = 2; *p != EOS; p++)
X    if (*p == '/')
X      ++n_dirs;
X
X  if (! (dirs = (struct dirmatch *) calloc (n_dirs, sizeof (dirs[0]))))
X    report_progress (report, "\nmake_indexes(): calloc() failed", TRUE),
X    exit (11);
X
X  if (! (dirs[0].s =
X	 (char *) malloc ((strlen (ARCHIVE_DIR) +
X			   strlen (DEFAULT_ARCHIVE) + 2) * sizeof (char))))
X    report_progress (report, "\nmake_indexes(): malloc() failed", TRUE),
X    exit (11);
X  sprintf (dirs[0].s, "%s/%s", ARCHIVE_DIR, DEFAULT_ARCHIVE);
X
X  for (i = 1, p = farch_dir; i < n_dirs; i++) {
X    int len;
X
X    /* Find end of the directory - slash or end of string */
X    for (q = p; *q != EOS && *q != '/'; q++);
X    len = q - p;
X
X    if (i == 1) {
X      if (! (dirs[i].s =
X	     (char *) malloc ((strlen (ARCHIVE_DIR) + len + 2) *
X			      sizeof (char))))
X	report_progress (report, "\nmake_indexes(): malloc() failed", TRUE),
X	exit (11);
X      sprintf (dirs[i].s, "%s/%.*s", ARCHIVE_DIR, len, p);
X    }
X    else {
X      if (! (dirs[i].s = (char *) malloc ((strlen (dirs[i - 1].s) + len + 2) *
X					  sizeof (char))))
X	report_progress (report, "\nmake_indexes(): malloc() failed", TRUE),
X	exit (11);
X      sprintf (dirs[i].s, "%s/%.*s", dirs[i - 1].s, len, p);
X    }
X
X    /* In case there are extra slashes, skip them */
X    for (; *q != EOS && *q == '/'; q++);
X    p = q;
X
X    if (*p == EOS)
X      break;            /* premature end of string - too many /'s */
X  }
X
X  for (i = 0; i < n_dirs; i++) {
X    char indexf [MAX_LINE], dirf [MAX_LINE];
X    char line [MAX_LINE], arch [MAX_LINE], fullpath [MAX_LINE];
X    BOOLEAN missing;
X    FILE *fp;
X
X    if (!dirs[i].s)
X      continue;
X
X    /* Make INDEX and DIR files if not already there */
X    sprintf (indexf, "%s/%s", dirs[i].s, INDEX);
X    sprintf (dirf, "%s/%s", dirs[i].s, DIRF);
X    if (access (indexf, 0))
X      close (creat (indexf, 0644 & (0644 ^ otoi (mask))));
X    if (access (dirf, 0))
X      close (creat (dirf, 0644 & (0644 ^ otoi (mask))));
X
X    /* Read the index file for i, set match fields to TRUE for j >= i if dir
X       for j is not in there. */
X    OPEN_FILE (fp, indexf, "r", "make_indexes");
X
X    for (j = i; j < n_dirs; j++)
X      dirs[j].match = FALSE;
X
X    while (!feof (fp)) {
X      arch[0] = fullpath[0] = RESET (line);
X      fgets (line, MAX_LINE - 2, fp);
X      sscanf (line, "%s %s", arch, fullpath);
X      for (j = i; j < n_dirs; j++)
X        if (dirs[j].s && !strcmp (fullpath, dirs[j].s))
X          dirs[j].match = TRUE;
X    }
X    fclose (fp);
X
X    /* Set 'missing' if any j doesn't have match set. Add lines if missing. */
X    missing = FALSE;
X    for (j = i; j < n_dirs; j++)
X      if (!dirs[j].match)
X        missing = TRUE;
X
X    if (missing) {
X      OPEN_FILE (fp, indexf, "a", "make_indexes");
X      for (j = i; j < n_dirs; j++)
X        if (dirs[j].s && !dirs[j].match)
X          fprintf (fp, "%s %s %s\n", strrchr (dirs[j].s, '/') + 1, dirs[j].s,
X		   (password ? password : ""));
X      fclose (fp);
X    }
X    free ((char *) dirs[i].s);
X  }
X  free ((struct dirmatch *) dirs);
X  return TRUE;
X}
X
X/*
X  Insert 'nwords' to 's' starting at 'index' in 'words'. Return the total
X  length of the words inserted; blanks are inserted between words.
X  While making space for the new words, 'replace' so many number of characters.
X*/
X
Xint insert_word (char *s, char **words, int nwords, int index, int replace)
X{
X  int i, j, nbytes = 0;
X  char *c = NULL;
X
X  for (i = 0; i < nwords; i++)
X    nbytes += strlen (words [index + i]) + 1;
X  if (nbytes < replace)
X    for (i = 0; i < replace; i++)
X      s[i] = ' ';
X  else
X    for (i = 0; i < nbytes - replace; i++)
X      for (c = s + strlen (s); c >= s + replace; c--)
X	*(c + 1) = *c;
X  for (i = 0, j = 0; i < nwords; i++)
X    memcpy (s + j, words [index + i], strlen (words [index + i])),
X    j += strlen (words [index + i]) + 1,
X    *(s + j - 1) = ' ';
X  return nbytes >= replace ? nbytes : replace;
X}
X
X/*
X  Skip over words in a string and return the address of the beginning of the
X  requested word, or NULL if the word is beyond the end of string.
X*/
X
Xchar *skip_to_word (char *s, int word)
X{
X  BOOLEAN quote;
X
X  while (*s != EOS && isspace (*s))	/* Skip to first word */
X    ++s;
X  while ((--word)) {
X    quote = FALSE;
X    while ((*s != EOS && !isspace (*s)) || (isspace (*s) && quote)) {
X      if (*s == '\"' || *s == '\'')
X	quote = !quote;
X      ++s;
X    }
X    while (*s != EOS && isspace (*s))
X      ++s;
X  }
X  if (*s == EOS)
X    return NULL;
X  return s;
X}
X
X/*
X  Some UNIXes do not provide strdup().
X*/
X
Xchar *mystrdup (char *s)
X{
X  char *r;
X
X  r = (char *) malloc ((strlen (s) + 1) * sizeof (char));
X  if (r)
X    strcpy (r, s);
X  return r;
X}
X
X#ifdef NEED_VSPRINTF
Xint vsprintf (dest, pat, args)
Xchar *dest, *pat, *args;
X{
X    FILE fakebuf;
X
X    fakebuf._ptr = dest;
X    fakebuf._cnt = 32767;
X#ifndef _IOSTRG
X#define _IOSTRG 0
X#endif
X    fakebuf._flag = _IOWRT|_IOSTRG;
X    _doprnt(pat, args, &fakebuf);
X    putc('\0', &fakebuf);
X    return 0;
X}
X#endif
*-*-END-of-src/misc.c-*-*
echo x - src/pqueue.c
sed 's/^X//' >src/pqueue.c <<'*-*-END-of-src/pqueue.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                    MAIL QUEUE PROCESSING ROUTINES                        |
X  |                                                                          |
X  |                             Version 1.1                                  |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  The 'system' mailmethod is used to attempt another delivery of the
X  specified files. If it cannot be done, the files are queued again.
X*/
X
X#include <stdio.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/types.h>
X#include <sys/stat.h>
X#if !defined (sequent) && !defined (__NeXT__) && !defined (__convex__) && \
X !defined (apollo) && !defined (i386) && !defined (unknown_port)
X# include <sys/termio.h>
X#endif
X#ifndef sun
X# include <sys/ioctl.h>
X#endif
X#include <fcntl.h>
X#include <signal.h>
X#include "defs.h"
X#include "struct.h"
X#include "global.h"
X#include "pqueue.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X#ifdef __STDC__
Xextern char *tsprintf (char *, ...);
X#else
Xextern char *tsprintf ();
X#endif
Xextern int  _getopt (int, char **, char *);
Xextern void init_signals (void);
Xextern void catch_signals (void); 
Xextern void report_progress (FILE *, char *, int);
Xextern BOOLEAN sysmail (char *);
Xextern int  lock_file (char *, int, int, BOOLEAN);
Xextern void unlock_file (int);
Xextern int  otoi (char *);
X
Xvoid   main (int, char **, char **);
Xvoid   usage (void);
Xint    gexit (int);
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  char *options = "eD", *mask;
X  int c;
X  FILE *f;
X  struct stat stat_buf;
X  extern int optind;
X  extern char *getenv();
X
X  prog = argv[0];
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X    case 'e': tty_echo = TRUE; break;
X    case 'D': debug = TRUE; break;
X    case '?':
X    default:
X      usage ();
X  }
X  if (optind == argc)
X    fprintf (stderr, "pqueue: filename(s) missing.\n"),
X    exit (3);
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (PQUEUE_LOCK_FILE, O_RDWR, 0, FALSE)) < 0)
X    fprintf (stderr, "pqueue: Unable to lock %s. Aborting.\n",
X	     PQUEUE_LOCK_FILE),
X    exit (1);
X#endif
X  init_signals();
X  catch_signals();
X#if defined (TIOCNOTTY) && defined (SIGTTOU) && defined (SIGTTIN)
X  if (!tty_echo)
X    if ((c = open ("/dev/tty", 2)) >=0)
X      ioctl (c, TIOCNOTTY, 0),
X      close (c);
X#endif
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  if ((report = fopen (REPORT_PQUEUE, "a")) == NULL)
X    fprintf (stderr, "pqueue: Could not open %s\n", REPORT_PQUEUE),
X    exit (1);
X  chmod (REPORT_PQUEUE, 384);
X#endif
X  if ((f = fopen (PID_PQUEUE, "w")) != NULL)
X    fprintf (f, "%d", getpid()),
X    fclose (f);
X  signal (SIGINT, (void (*)()) gexit);
X  while (--argc >= optind) { /* main loop */
X    if (stat (argv [argc], &stat_buf))
X      report_progress (report, tsprintf ("\nmain(): Could not stat %s",
X					 argv [argc]), TRUE),
X      gexit (1);
X    if (sysmail (argv [argc]))
X      if (unlink (argv [argc]))
X 	report_progress (report, tsprintf ("\nmain(): Could not unlink file %s",
X					   argv [argc]), TRUE),
X	gexit (1);
X  }
X#ifdef SYSLOG
X  closelog ();
X#else
X  fclose (report);
X#endif
X  gexit (0);
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: pqueue [-e] [-D] <files>\n\
X-e: Echo reports to the screen.\n\
X-D: Turn debug on.\n");
X  exit (3);
X}
X
X/*
X  Graceful exit. Remove pid file.
X*/
X
Xint gexit (int exitcode)
X{
X  unlink (PID_PQUEUE);
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X#endif
X  exit (exitcode);
X}
*-*-END-of-src/pqueue.c-*-*
echo x - src/regerror.c
sed 's/^X//' >src/regerror.c <<'*-*-END-of-src/regerror.c-*-*'
X/*
X  Tasos Kotsikonas (8/20/92): Changed function so that it stores the error
X  message to a global variable.
X*/
X
X#include <stdio.h>
Xchar *regerr;
X
Xvoid
Xregerror(s)
Xchar *s;
X{
X  regerr = (char *) malloc ((strlen (s) + 1) * sizeof (char));
X  strcpy (regerr, s);
X}
*-*-END-of-src/regerror.c-*-*
echo x - src/regex.c
sed 's/^X//' >src/regex.c <<'*-*-END-of-src/regex.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                         REGULAR EXPRESSIONS                              |
X  |                                                                          |
X  |                             Version 1.0                                  |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  Author: Tasos Kotsikonas (tasos@cs.bu.edu)
X  Disclaimer: All proper disclaimers apply; these include death, damage of any
X	      kind to anything and anyone, and this kind of legal garbage.
X  Copying: You may copy, alter and redistribute this software as you see fit,
X  	   but you may not sell it for profit.
X
X  Do pattern matching of two flavors: strict egrep(1), or extended egrep(1).
X  If the symbol egrep is #define'd below, strict egrep(1) syntax is used.
X
X  The main routine is re_strcmp() which takes a regular expression, a
X  string to match against, and a string specifying which of the matched
X  subparts are to be used to form a new string -- if this string is NULL no
X  such action is taken.
X
X  The new string is stored in the same space, so this string should be
X  long enough not to cause memory overwrites. To be able to do this match
X  substitution, the desired subparts of the regular expression should
X  be enclosed in ( and ). Each subpart is referenced to as \n where n is
X  a digit from 1 to 9.
X
X  For example, the following call:
X
X  strcpy (match, "\\2@\\1.UUCP");
X  re_strcmp ("[^!@]*!([^!@.]*)!([^!@]*)@.*", "GATE!HOP!USER@UUCP.SOME.COM",
X  	     match);
X
X  will return TRUE and store in 'match' "USER@HOP.UUCP". See the man page
X  for Henry Spencer's routines included for more information.
X
X  In extended mode, regular expressions may use the logical operators ~ (not),
X  & (and) and group expressions further with < and >. Because of the latter,
X  < and > may not be used in the ed(1) sense (\< is the default in the
X  context these routines were developed, and \> is useless in the same context).
X  To use any of these characters literally, precede them with a backslash (\).
X
X  For example:
X
X  re_strcmp ("~<.*\.COM|.*\.EDU>&~TASOS*", "TASOS@FOO.US", NULL)
X
X  will return FALSE.
X
X  re_strcmp() returns TRUE on success, FALSE if no match was found, or
X  -1 on error. In the latter case, the (char *) variable regerr contains the
X  actual error message.
X
X  The system uses modified pattern matching routines written by Henry Spencer
X  (Copyright (c) 1986 by University of Toronto) -- actually only regerror.c
X  was modified. Many thanks and kudos to Henry for this wonderful piece of code.
X
X  It is possible to use the ls(1) style wild characters * and ? by altering
X  the symbols START and QMARK below.
X
X  It is also possible to turn off ed(1) style pattern matching (i.e. turn
X  off the meaning of [ ] { } etc.) by using the function escape_re() where
X  marked with ###.
X
X  To test these routines compile with -Dtest and run independently as follows:
X
X		% cc -Dtest -I. regexp.c regsub.c regex.c regerror.c
X		% a.out 'regular-expression' file [match]
X
X  NOTICE: Spencer's $ matches the enf-of-line without mathing a newline
X  character. When compiling with -Dtest, $ is replaced with .$
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <string.h>
X#include <ctype.h>
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#include "regexp.h"
X
X/*
X#define egrep	Define it if you want to use strict egrep(1) regular expressions
X*/
X
X#define MAXLENGTH	1024	/* Maximum length of each regular expression */
X#define EOS		'\0'
X
Xint  re_strcmp (char *, char *, char *);
Xint  prevch (char *, char *);
Xint  nextch (char *);
X
X#ifdef test
Xint  main (int, char **);
Xvoid workaround (char *);
X#else
Xextern void report_progress (FILE *, char *, int);
Xextern FILE *report;
X#endif
X#ifndef egrep
Xvoid escape_re (char *);
Xchar *convert_re (char *);
Xint  icp (int);
Xint  push (int);
Xint  pop (void);
Xint  do_op (int, int, int);
Xvoid pop_op (void);
Xint  new_op (int);
Xint  eval (void);
Xint  isop (char *, char *);
X
X# define STAR		"*"	/* If ".*" then the meaning is that of ls(1) */
X# define QMARK		"?"	/* If "." then the meaning is that of ls(1) */
X# define LGROUPCH	'<'	/* Avoid ( and ) */
X# define RGROUPCH	'>'
X# define OR		1	/* Operators should be > 0 */
X# define AND		2
X# define NOT		3
X# define LPAREN		4
X# define RPAREN		5
X
Xtypedef struct _operator_stack {
X  int op;
X  int isp;	/* In-stack priority */
X  struct _operator_stack *next, *prev;
X} OPERATOR_STACK;
X
Xtypedef struct _operand_stack {
X  int val;
X  struct _operand_stack *next, *prev;
X} OPERAND_STACK;
X
XOPERATOR_STACK *op_top;
XOPERAND_STACK *val_top;
X#endif
X
Xint pliteral, literal, nliteral;
X
Xextern char *regerr;
X
X#ifdef test
Xint main (int argc, char **argv)
X{
X  FILE *f;
X  char s [1024], matches [1024];
X  int match;
X
X  if (argc < 3)
X    printf ("Usage: %s 'regular-expression' file [match]\n", argv[0]), exit (1);
X  if ((f = fopen (argv[2], "r")) == NULL)
X    printf ("%s: ", argv[2]), fflush (stdout), perror (""), exit (1);
X  while (!feof (f)) {
X    memset (s, EOS, sizeof (s));
X    fgets (s, sizeof (s) - 1, f);
X    if (argc > 3)
X      strcpy (matches, argv[3]);
X    if ((match = re_strcmp (argv[1], s, matches)) > 0)
X      printf ("MATCHES:%s\n", matches),
X      printf ("%s", s);
X    else if (match < 0)
X      printf ("Error in regular expression\n"),
X      exit (1);
X  }
X  fclose (f);
X  exit (0);
X}
X#endif
X
X/*
X  Check 'subject' againt the 'regexpr'. Return 1 on match, 0 if no match,
X  or -1 on error. To negegate a regular expression precede it with '~';
X  multiple regular expressions are separated by '|' or '&' (logical OR and
X  AND) and may be grouped with LGROUPCH and RGROUPCH. To escape the key
X  characters LGROUPCH * ? | & RGROUPCH use \.
X*/
X
Xint re_strcmp (char *regexpr, char *subject, char *result)
X{
X  char *re, *readdr, *s, _re[MAXLENGTH], matches [1024], *error;
X  int op, i;
X  regexp *cmp;
X
X  s = (char *) malloc ((strlen (regexpr) + 1) * sizeof (char));
X  strcpy (s, regexpr);
X#ifdef test
X  workaround (s);	/* Work around bugs with Spencer's code */
X#endif
X#ifndef egrep
X/*###		Do not escape ed(1) special characters.
X  escape_re (s);
X*/
X  readdr = re = convert_re (s);
X#else
X  re = s;
X#endif
X  do {
X#ifndef egrep
X    if ((op = isop (re, readdr))) {
X      if (new_op (op)) {
X	eval ();	/* Empty stacks */
X	return -1;
X      }
X      ++re;
X    }
X    else 
X#endif
X    {
X      i = 0;
X      while (*re != EOS && i < MAXLENGTH
X#ifndef egrep
X	     && !isop (re, readdr)
X#endif
X	    )
X	_re [i++] = *re,
X	++re;
X      if (i == MAXLENGTH)	/* Overflow */
X	return -1;
X      _re [i] = EOS;
X      if (! (cmp = (regexp *) regcomp (_re))) {
X#ifndef test
X	error = (char *) malloc ((strlen (regerr) + strlen (_re) + 7) * 
X				 sizeof (char));
X	sprintf (error, "RE %s: %s\n", _re, regerr);
X	report_progress (report, error, 1);
X#else
X        printf ("RE %s: %s\n", _re, regerr);
X#endif
X	free ((char *) regerr);
X	free ((char *) error);
X#ifndef egrep
X	eval ();	/* Empty stacks */
X	free ((char *) readdr);
X#endif
X        return -1;
X      }
X      if (regexec (cmp, subject)) {
X	if (result)
X	  regsub (cmp, result, matches),
X	  strcpy (result, matches);
X	free ((regexp *) cmp);
X#ifndef egrep
X	push (1);
X#else
X	free ((char *) s);
X	return 1;
X#endif
X      }
X      else {
X	free ((regexp *) cmp);
X#ifndef egrep
X	push (0);
X#else
X	free ((char *) s);
X	return 0;
X#endif
X      }
X    }
X  } while (*re != EOS);
X#ifndef egrep
X  free ((char *) readdr);
X  free ((char *) s);
X  return eval ();
X#endif
X}
X
X/*
X  Scan 's' and escape the following characters: [ ] < > { } , ; . ^ $ + -
X  for regular expression matching.
X
X  To be used if one does not want ed(1) style pattern matching. Currently, this
X  function is unused.
X*/
X
X#ifndef egrep
Xvoid escape_re (char *s)
X{
X  char *r;
X
X  while (*s != EOS) {
X    switch (*s) {
X    case '[': case ']': case '<': case '>': case '+': case '-':
X    case ';': case ',': case '.': case '^': case '$': case '{': case '}':
X      r = s + strlen (s);       /* Start from the end */
X      while (r != s)
X        *(r + 1) = *r,
X        --r;
X      *(r + 1) = *r;
X      *r = '\\';
X      ++s;
X      break;
X    }
X    ++s;
X  }
X}
X
X/*
X  Possibly convert the wild characters * and ? to ed(1) regular expressions.
X  Handle escaped characters.
X*/
X
Xchar *convert_re (char *re)
X{
X  char *r, *b = re;
X  int i = 0;
X
X  if (! (r = (char *) malloc (sizeof (char))))
X# ifndef test
X    report_progress (report, "\nconvert_re(): malloc() failed", 1),
X    exit (11);
X# else
X    fprintf (stderr, "convert_re(): malloc() failed\n"),
X    exit (11);
X# endif
X  while (*re != EOS) {
X    prevch (re, b);	/* See if *re is literal */
X    switch (*re) {
X    case '\\':
X      if (re != b && literal) {
X	if (! (r = (char *) realloc (r, (i + 2) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	strncpy (r + i, "\\\\", 2);
X	i += 2;
X      }
X      break;
X    case '?':		/* Match one character */
X      if (re != b && literal) {	/* Literal */
X	if (! (r = (char *) realloc (r, (i + 1) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	*(r + i) = *re;
X	++i;
X      }
X      else {
X	if (! (r = (char *) realloc (r, (i + strlen (QMARK)) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	strncpy (r + i, QMARK, strlen (QMARK));
X	i += strlen (QMARK);
X      }
X      break;
X    case '*':		/* Match multiple characters */
X      if (re != b && literal) {	/* Literal */
X	if (! (r = (char *) realloc (r, (i + 2) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	strncpy (r + i, "\\*", 2);
X	i += 2;
X      }
X      else {
X	if (! (r = (char *) realloc (r, (i + strlen (STAR)) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	strncpy (r + i, STAR, strlen (STAR));
X	i += strlen (STAR);
X      }
X      break;
X    default:
X      if (re != b && literal) {	/* carry over */
X	if (! (r = (char *) realloc (r, (i + 2) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	*(r + i) = *(re - 1);
X	*(r + i + 1) = *re;
X	i += 2;
X      }
X      else {
X	if (! (r = (char *) realloc (r, (i + 1) * sizeof (char))))
X# ifndef test
X	  report_progress (report, "\nconvert_re(): realloc() failed", 1),
X	  exit (11);
X# else
X	  fprintf (stderr, "convert_re(): realloc() failed\n"),
X	  exit (11);
X# endif
X	*(r + i) = *re;
X	++i;
X      }
X    }
X    ++re;
X  }
X  if (! (r = (char *) realloc (r, (i + 1) * sizeof (char))))
X# ifndef test
X    report_progress (report, "\nconvert_re(): realloc() failed", 1),
X    exit (11);
X# else
X    fprintf (stderr, "convert_re(): realloc() failed\n"),
X    exit (11);
X# endif
X  *(r + i) = EOS;
X  return r;
X}
X
X/*
X  Return the in-coming priority of an operator.
X*/
X
Xint icp (int op)
X{
X  if (op == OR || op == AND) return 1;
X  if (op == LPAREN || op == NOT) return 4;
X  return 0;
X}
X
X/*
X  Push a new operand onto OPERAND_STACK.
X*/
X
Xint push (int val)
X{
X  OPERAND_STACK *s;
X
X  if (! (s = (OPERAND_STACK *) malloc (sizeof (OPERAND_STACK))))
X# ifndef test
X    report_progress (report, "\npush(): malloc() failed", 1),
X    exit (11);
X# else
X    fprintf (stderr, "push(): malloc() failed\n"),
X    exit (11);
X# endif
X  s->val = val;
X  s->prev = val_top;
X  s->next = NULL;
X  if (val_top)
X    val_top->next = s;
X  val_top = s;
X  return val;
X}
X
X/*
X  Pop an operand from OPERAND_STACK. Return 0 or 1, or -1 on error.
X*/
X
Xint pop ()
X{
X  int val;
X
X  if (!val_top)	/* Empty stack */
X    return -1;
X  val = val_top->val;
X  if (val_top->prev)
X    val_top = val_top->prev,
X    free ((OPERAND_STACK *) val_top->next),
X    val_top->next = NULL;
X  else
X    free ((OPERAND_STACK *) val_top),
X    val_top = NULL;
X  return val;
X}
X
X/*
X  Perform a boolean operation and return the result, or -1 on error.
X*/
X
Xint do_op (int op, int val1, int val2)
X{
X  if (val1 < 0 || val2 < 0)
X    return -1;
X  if (op == OR)
X    return val1 | val2;
X  if (op == AND)
X    return val1 & val2;
X  return !val1;
X}
X
X/*
X  Pop an operator. An operator will always be present.
X*/
X
Xvoid pop_op ()
X{
X  OPERATOR_STACK *s;
X
X  s = op_top;
X  op_top = op_top->prev;
X  if (op_top)
X    op_top->next = NULL;
X  free ((OPERATOR_STACK *) s);
X}
X
X/*
X  Process a new operator: push it anyway, or push it after popping other
X  operators with higher priority. Return -1 on error condition.
X*/
X
Xint new_op (int op)
X{
X  OPERATOR_STACK *s;
X  int res;
X
X  if (op < OR)
X    return -1;
X  if (op == RPAREN) {
X    do {	/* Pop and process operators till LPAREN */
X      if (!op_top)
X	return -1;
X      if (op_top->op != LPAREN) {
X	if (op_top->op == OR || op_top->op == AND)
X	  res = push (do_op (op_top->op, pop (), pop ()));
X	else	/* NOT */
X	  res = push (do_op (op_top->op, pop (), 0));
X	if (res < 0)	/* Error with operands */
X	  return res;
X	pop_op ();	/* Pop processed operator */
X	if (!op_top)
X	  return -1;
X      }
X    } while (op_top->op != LPAREN);
X    pop_op ();	/* LPAREN */
X  }
X  else {	/* Push new operator */
X    while (op_top && op_top->isp >= icp (op)) {	/* Process op w/ > priority */
X      if (op_top->op == OR || op_top->op == AND)
X	res = push (do_op (op_top->op, pop (), pop ()));
X      else    /* NOT */
X	res = push (do_op (op_top->op, pop (), 0));
X      if (res < 0)
X	return res;
X      pop_op ();
X    }
X    if (! (s = (OPERATOR_STACK *) malloc (sizeof (OPERATOR_STACK))))
X# ifndef test
X      report_progress (report, "\nnew_op(): malloc() failed", 1),
X      exit (11);
X# else
X      fprintf (stderr, "new_op(): malloc() failed\n"),
X      exit (11);
X# endif
X    s->op = op;
X    if (op == OR) s->isp = 1;
X    else if (op == AND) s->isp = 1;
X    else if (op == NOT) s->isp = 2;
X    else s->isp = 0;
X    s->prev = op_top;
X    s->next = NULL;
X    if (op_top)
X      op_top->next = s;
X    op_top = s;
X  }
X  return 0;
X}
X
X/*
X  Process the remaining operators and values. Return -1 on error.
X*/
X
Xint eval ()
X{
X  OPERATOR_STACK *op;
X  int res;
X
X  if (!op_top && !val_top)
X    return 0;
X  while (op_top) {
X    if (op_top->op != AND && op_top->op != OR && op_top->op != NOT) {
X      if (op_top->op < OR || op_top->op > RPAREN)
X# ifndef test
X	report_progress (report, "Internal error: unexpected op", 1);
X# else
X	printf ("Internal error: unexpected op %d\n", op_top->op);
X# endif
X      return -1;
X    }
X    if (op_top->op == OR || op_top->op == AND)
X      res = push (do_op (op_top->op, pop (), pop ()));
X    else	/* NOT */
X      res = push (do_op (op_top->op, pop (), 0));
X    if (res < 0)
X      return res;
X    op = op_top;
X    op_top = op_top->prev;
X    free ((OPERATOR_STACK *) op);
X  }
X  if (!val_top) {
X# ifndef test
X    report_progress (report, "Internal error: empty operand stack", 1);
X# else
X    printf ("Internal error: empty operand stack\n");
X# endif
X    return -1;
X  }
X  return pop ();
X}
X
X/*
X  Return the operator id if indeed 's' points to an operator. Return -1
X  on error.
X*/
X
Xint isop (char *s, char *b)
X{
X  char pch, nch;
X
X  pch = prevch (s, b);
X  if (literal) return 0;
X  nch = nextch (s);
X  if (*s == LGROUPCH)
X    if ((pch == '|' || pch == '&' || pch == '~' || pch == LGROUPCH) &&
X	!pliteral && ((nch != '|' && nch != '&' && nch != EOS) || nliteral))
X      return LPAREN;
X    else
X      return -1;
X  if (*s == RGROUPCH)
X    if (((pch != '|' && pch != '&' && pch != '~') || pliteral) && !nliteral &&
X	(nch == RGROUPCH || nch == '&' || nch == '|' || nch == EOS))
X      return RPAREN;
X    else
X      return -1;
X  if (*s == '~')
X    if ((pch == LGROUPCH || pch == '|' || pch == '&' || pch == '~') &&
X	!pliteral &&
X	((nch != '|' && nch != '&' && nch != RGROUPCH && nch != EOS) ||
X	 nliteral))
X      return NOT;
X    else
X      return -1;
X  if (*s == '|' || *s == '&')
X    if (((pch != '~' && pch != '|' && pch != '&') || pliteral) &&
X	((nch != '|' && nch != '&' && nch != RGROUPCH &&
X	  nch != EOS) || nliteral))
X      return (*s == '|' ? OR : AND);
X    else
X      return -1;
X  return 0;
X}
X#endif
X
X/*
X  Return the previous character from the current position in the
X  string. Set 'pliteral' to 1 if that previous character is escaped with \ and
X  'literal' to 1 if the current character is escaped with \.
X  In the comments, ^ means "beginning of the string", ? matches any character
X  except \, * matches absolutely any character and x is the current position.
X*/
X
Xint prevch (char *s, char *b)
X{
X  return
X    ((s) <= (b) ? 
X     (pliteral = literal = 0, *(s)) : /* ^x */
X     (((s) - 1) == (b) ? 
X      (*((s) - 1) == '\\' ? (pliteral = !(literal = 1), *((s) - 1)) /* ^\x */
X       : (pliteral = literal = 0, *((s) - 1))) /* ^?x */
X      : (*(s) == '\\' ? 
X	 (*((s) - 2) == '\\' ? (pliteral = !(literal = 0), *((s) - 1)) /* \*\ */
X	  : (*((s) - 1) == '\\' ? (pliteral = !(literal = 1), *((s) - 2)) /* ?\\ */
X	     : (pliteral = literal = 0, *((s) - 1)))) /* ??\ */
X	 : (*((s) - 1) == '\\' ? 
X	    (*((s) - 2) == '\\' ? 
X	     (((s) - 2) == (b) ? (pliteral = !(literal = 0), *((s) - 1)) /* ^\\x */
X	      : (*((s) - 3) == '\\' ? (pliteral = literal = 1,*((s) - 2)) /* \\\x */
X		 : (pliteral = !(literal = 0), *((s) - 1)))) /* ?\\x */
X	     : (((s) - 2) == (b) ? (pliteral = !(literal = 1),*((s) - 2)) /* ^?\x */
X		: (*((s) - 3) == '\\' ? (pliteral = literal = 1, *((s) - 2)) /* \?\x */
X		   : (pliteral = !(literal = 1),  *((s) - 2))))) /* ??\x */
X	    : (*((s) - 2) == '\\' ? (pliteral = !(literal = 0),*((s) - 1)) /* \?x */
X	       : (pliteral = literal = 0, *((s) - 1))))))); /* ??x */
X}
X
X/*
X  Return the next character from the current position. The current character
X  is guarranteed not to be a literal. In the comments below * matches
X  absolutely any character, ? matches anything but EOS, $ is the EOS and
X  x marks the current position. Also set 'nliteral' to 1 if the next character
X  is escaped with \.
X*/
X
Xint nextch (char *s)
X{
X  return
X    (*((s) + 1) == EOS ? (nliteral = 0, EOS) /* x$ */
X     : (*((s) + 1) == '\\' ? 
X	(*((s) + 2) == EOS ? (nliteral = 0, *((s) + 1)) /* x\$ */
X	 : (nliteral = 1, *((s) + 2))) /* x\? */
X	: (nliteral = 0, *((s) + 1)))); /* x* */
X}
X#ifdef test
X
X/*
X  Henry Spencer's code requires that $ be converted to .$
X*/
X
Xvoid workaround (char *s)
X{
X  char *r, *b = s;
X
X  while (*s != EOS) {
X    switch (*s) {
X    case '$':
X      prevch (s, b);	/* See if thi char is a literal */
X      if (literal) {
X	++s;
X	continue;
X      }
X      r = s + strlen (s);       /* Start from the end */
X      while (r != s)
X        *(r + 1) = *r,
X        --r;
X      *(r + 1) = *r;
X      *r = '.';
X      ++s;
X      break;
X    }
X    ++s;
X  }
X}
X#endif
*-*-END-of-src/regex.c-*-*
echo x - src/regexp.c
sed 's/^X//' >src/regexp.c <<'*-*-END-of-src/regexp.c-*-*'
X/*
X * regcomp and regexec -- regsub and regerror are elsewhere
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X *
X * Beware that some of this code is subtly aware of the way operator
X * precedence is structured in regular expressions.  Serious changes in
X * regular-expression syntax might require a total rethink.
X */
X#include <stdio.h>
X#include "regexp.h"
X#include "regmagic.h"
X
X/*
X * The "internal use only" fields in regexp.h are present to pass info from
X * compile to execute that permits the execute phase to run lots faster on
X * simple cases.  They are:
X *
X * regstart	char that must begin a match; '\0' if none obvious
X * reganch	is the match anchored (at beginning-of-line only)?
X * regmust	string (pointer into program) that match must include, or NULL
X * regmlen	length of regmust string
X *
X * Regstart and reganch permit very fast decisions on suitable starting points
X * for a match, cutting down the work a lot.  Regmust permits fast rejection
X * of lines that cannot possibly match.  The regmust tests are costly enough
X * that regcomp() supplies a regmust only if the r.e. contains something
X * potentially expensive (at present, the only such thing detected is * or +
X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
X * supplied because the test in regexec() needs it and regcomp() is computing
X * it anyway.
X */
X
X/*
X * Structure for regexp "program".  This is essentially a linear encoding
X * of a nondeterministic finite-state machine (aka syntax charts or
X * "railroad normal form" in parsing technology).  Each node is an opcode
X * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
X * all nodes except BRANCH implement concatenation; a "next" pointer with
X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
X * have one of the subtle syntax dependencies:  an individual BRANCH (as
X * opposed to a collection of them) is never concatenated with anything
X * because of operator precedence.)  The operand of some types of node is
X * a literal string; for others, it is a node leading into a sub-FSM.  In
X * particular, the operand of a BRANCH node is the first node of the branch.
X * (NB this is *not* a tree structure:  the tail of the branch connects
X * to the thing following the set of BRANCHes.)  The opcodes are:
X */
X
X/* definition	number	opnd?	meaning */
X#define	END	0	/* no	End of program. */
X#define	BOL	1	/* no	Match "" at beginning of line. */
X#define	EOL	2	/* no	Match "" at end of line. */
X#define	ANY	3	/* no	Match any one character. */
X#define	ANYOF	4	/* str	Match any character in this string. */
X#define	ANYBUT	5	/* str	Match any character not in this string. */
X#define	BRANCH	6	/* node	Match this alternative, or the next... */
X#define	BACK	7	/* no	Match "", "next" ptr points backward. */
X#define	EXACTLY	8	/* str	Match this string. */
X#define	NOTHING	9	/* no	Match empty string. */
X#define	STAR	10	/* node	Match this (simple) thing 0 or more times. */
X#define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */
X#define	OPEN	20	/* no	Mark this point in input as start of #n. */
X			/*	OPEN+1 is number 1, etc. */
X#define	CLOSE	30	/* no	Analogous to OPEN. */
X
X/*
X * Opcode notes:
X *
X * BRANCH	The set of branches constituting a single choice are hooked
X *		together with their "next" pointers, since precedence prevents
X *		anything being concatenated to any individual branch.  The
X *		"next" pointer of the last BRANCH in a choice points to the
X *		thing following the whole choice.  This is also where the
X *		final "next" pointer of each individual branch points; each
X *		branch starts with the operand node of a BRANCH node.
X *
X * BACK		Normal "next" pointers all implicitly point forward; BACK
X *		exists to make loop structures possible.
X *
X * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular
X *		BRANCH structures using BACK.  Simple cases (one character
X *		per match) are implemented with STAR and PLUS for speed
X *		and to minimize recursive plunges.
X *
X * OPEN,CLOSE	...are numbered at compile time.
X */
X
X/*
X * A node is one char of opcode followed by two chars of "next" pointer.
X * "Next" pointers are stored as two 8-bit pieces, high order first.  The
X * value is a positive offset from the opcode of the node containing it.
X * An operand, if any, simply follows the node.  (Note that much of the
X * code generation knows about this implicit relationship.)
X *
X * Using two bytes for the "next" pointer is vast overkill for most things,
X * but allows patterns to get big without disasters.
X */
X#define	OP(p)	(*(p))
X#define	NEXT(p)	(((*((p)+1)&0377)<<8) + *((p)+2)&0377)
X#define	OPERAND(p)	((p) + 3)
X
X/*
X * See regmagic.h for one further detail of program structure.
X */
X
X
X/*
X * Utility definitions.
X */
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X#define	FAIL(m)	{ regerror(m); return(NULL); }
X#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?')
X#define	META	"^$.[()|?+*\\"
X
X/*
X * Flags to be passed up and down.
X */
X#define	HASWIDTH	01	/* Known never to match null string. */
X#define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */
X#define	SPSTART		04	/* Starts with * or +. */
X#define	WORST		0	/* Worst case. */
X
X/*
X * Global work variables for regcomp().
X */
Xstatic char *regparse;		/* Input-scan pointer. */
Xstatic int regnpar;		/* () count. */
Xstatic char regdummy;
Xstatic char *regcode;		/* Code-emit pointer; &regdummy = don't. */
Xstatic long regsize;		/* Code size. */
X
X/*
X * Forward declarations for regcomp()'s friends.
X */
X#ifndef STATIC
X#define	STATIC	static
X#endif
XSTATIC char *reg();
XSTATIC char *regbranch();
XSTATIC char *regpiece();
XSTATIC char *regatom();
XSTATIC char *regnode();
XSTATIC char *regnext();
XSTATIC void regc();
XSTATIC void reginsert();
XSTATIC void regtail();
XSTATIC void regoptail();
X#ifdef STRCSPN
XSTATIC int strcspn();
X#endif
X
X/*
X - regcomp - compile a regular expression into internal code
X *
X * We can't allocate space until we know how big the compiled form will be,
X * but we can't compile it (and thus know how big it is) until we've got a
X * place to put the code.  So we cheat:  we compile it twice, once with code
X * generation turned off and size counting turned on, and once "for real".
X * This also means that we don't allocate space until we are sure that the
X * thing really will compile successfully, and we never have to move the
X * code and thus invalidate pointers into it.  (Note that it has to be in
X * one piece because free() must be able to free it all.)
X *
X * Beware that the optimization-preparation code in here knows about some
X * of the structure of the compiled regexp.
X */
Xregexp *
Xregcomp(exp)
Xchar *exp;
X{
X	register regexp *r;
X	register char *scan;
X	register char *longest;
X	register int len;
X	int flags;
X	extern char *malloc();
X
X	if (exp == NULL)
X		FAIL("NULL argument");
X
X	/* First pass: determine size, legality. */
X	regparse = exp;
X	regnpar = 1;
X	regsize = 0L;
X	regcode = &regdummy;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Small enough for pointer-storage convention? */
X	if (regsize >= 32767L)		/* Probably could be 65535L. */
X		FAIL("regexp too big");
X
X	/* Allocate space. */
X	r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
X	if (r == NULL)
X		FAIL("out of space");
X
X	/* Second pass: emit code. */
X	regparse = exp;
X	regnpar = 1;
X	regcode = r->program;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Dig out information for optimizations. */
X	r->regstart = '\0';	/* Worst-case defaults. */
X	r->reganch = 0;
X	r->regmust = NULL;
X	r->regmlen = 0;
X	scan = r->program+1;			/* First BRANCH. */
X	if (OP(regnext(scan)) == END) {		/* Only one top-level choice. */
X		scan = OPERAND(scan);
X
X		/* Starting-point info. */
X		if (OP(scan) == EXACTLY)
X			r->regstart = *OPERAND(scan);
X		else if (OP(scan) == BOL)
X			r->reganch++;
X
X		/*
X		 * If there's something expensive in the r.e., find the
X		 * longest literal string that must appear and make it the
X		 * regmust.  Resolve ties in favor of later strings, since
X		 * the regstart check works with the beginning of the r.e.
X		 * and avoiding duplication strengthens checking.  Not a
X		 * strong reason, but sufficient in the absence of others.
X		 */
X		if (flags&SPSTART) {
X			longest = NULL;
X			len = 0;
X			for (; scan != NULL; scan = regnext(scan))
X				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
X					longest = OPERAND(scan);
X					len = strlen(OPERAND(scan));
X				}
X			r->regmust = longest;
X			r->regmlen = len;
X		}
X	}
X
X	return(r);
X}
X
X/*
X - reg - regular expression, i.e. main body or parenthesized thing
X *
X * Caller must absorb opening parenthesis.
X *
X * Combining parenthesis handling with the base level of regular expression
X * is a trifle forced, but the need to tie the tails of the branches to what
X * follows makes it hard to avoid.
X */
Xstatic char *
Xreg(paren, flagp)
Xint paren;			/* Parenthesized? */
Xint *flagp;
X{
X	register char *ret;
X	register char *br;
X	register char *ender;
X	register int parno;
X	int flags;
X
X	*flagp = HASWIDTH;	/* Tentatively. */
X
X	/* Make an OPEN node, if parenthesized. */
X	if (paren) {
X		if (regnpar >= NSUBEXP)
X			FAIL("too many ()");
X		parno = regnpar;
X		regnpar++;
X		ret = regnode(OPEN+parno);
X	} else
X		ret = NULL;
X
X	/* Pick up the branches, linking them together. */
X	br = regbranch(&flags);
X	if (br == NULL)
X		return(NULL);
X	if (ret != NULL)
X		regtail(ret, br);	/* OPEN -> first. */
X	else
X		ret = br;
X	if (!(flags&HASWIDTH))
X		*flagp &= ~HASWIDTH;
X	*flagp |= flags&SPSTART;
X	while (*regparse == '|') {
X		regparse++;
X		br = regbranch(&flags);
X		if (br == NULL)
X			return(NULL);
X		regtail(ret, br);	/* BRANCH -> BRANCH. */
X		if (!(flags&HASWIDTH))
X			*flagp &= ~HASWIDTH;
X		*flagp |= flags&SPSTART;
X	}
X
X	/* Make a closing node, and hook it on the end. */
X	ender = regnode((paren) ? CLOSE+parno : END);	
X	regtail(ret, ender);
X
X	/* Hook the tails of the branches to the closing node. */
X	for (br = ret; br != NULL; br = regnext(br))
X		regoptail(br, ender);
X
X	/* Check for proper termination. */
X	if (paren && *regparse++ != ')') {
X		FAIL("unmatched ()");
X	} else if (!paren && *regparse != '\0') {
X		if (*regparse == ')') {
X			FAIL("unmatched ()");
X		} else
X			FAIL("junk on end");	/* "Can't happen". */
X		/* NOTREACHED */
X	}
X
X	return(ret);
X}
X
X/*
X - regbranch - one alternative of an | operator
X *
X * Implements the concatenation operator.
X */
Xstatic char *
Xregbranch(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char *chain;
X	register char *latest;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	ret = regnode(BRANCH);
X	chain = NULL;
X	while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
X		latest = regpiece(&flags);
X		if (latest == NULL)
X			return(NULL);
X		*flagp |= flags&HASWIDTH;
X		if (chain == NULL)	/* First piece. */
X			*flagp |= flags&SPSTART;
X		else
X			regtail(chain, latest);
X		chain = latest;
X	}
X	if (chain == NULL)	/* Loop ran zero times. */
X		(void) regnode(NOTHING);
X
X	return(ret);
X}
X
X/*
X - regpiece - something followed by possible [*+?]
X *
X * Note that the branching code sequences used for ? and the general cases
X * of * and + are somewhat optimized:  they use the same NOTHING node as
X * both the endmarker for their branch list and the body of the last branch.
X * It might seem that this node could be dispensed with entirely, but the
X * endmarker role is not redundant.
X */
Xstatic char *
Xregpiece(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char op;
X	register char *next;
X	int flags;
X
X	ret = regatom(&flags);
X	if (ret == NULL)
X		return(NULL);
X
X	op = *regparse;
X	if (!ISMULT(op)) {
X		*flagp = flags;
X		return(ret);
X	}
X
X	if (!(flags&HASWIDTH) && op != '?')
X		FAIL("*+ operand could be empty");
X	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
X
X	if (op == '*' && (flags&SIMPLE))
X		reginsert(STAR, ret);
X	else if (op == '*') {
X		/* Emit x* as (x&|), where & means "self". */
X		reginsert(BRANCH, ret);			/* Either x */
X		regoptail(ret, regnode(BACK));		/* and loop */
X		regoptail(ret, ret);			/* back */
X		regtail(ret, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '+' && (flags&SIMPLE))
X		reginsert(PLUS, ret);
X	else if (op == '+') {
X		/* Emit x+ as x(&|), where & means "self". */
X		next = regnode(BRANCH);			/* Either */
X		regtail(ret, next);
X		regtail(regnode(BACK), ret);		/* loop back */
X		regtail(next, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '?') {
X		/* Emit x? as (x|) */
X		reginsert(BRANCH, ret);			/* Either x */
X		regtail(ret, regnode(BRANCH));		/* or */
X		next = regnode(NOTHING);		/* null. */
X		regtail(ret, next);
X		regoptail(ret, next);
X	}
X	regparse++;
X	if (ISMULT(*regparse))
X		FAIL("nested *?+");
X
X	return(ret);
X}
X
X/*
X - regatom - the lowest level
X *
X * Optimization:  gobbles an entire sequence of ordinary characters so that
X * it can turn them into a single node, which is smaller to store and
X * faster to run.  Backslashed characters are exceptions, each becoming a
X * separate node; the code is simpler that way and it's not worth fixing.
X */
Xstatic char *
Xregatom(flagp)
Xint *flagp;
X{
X	register char *ret;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	switch (*regparse++) {
X	case '^':
X		ret = regnode(BOL);
X		break;
X	case '$':
X		ret = regnode(EOL);
X		break;
X	case '.':
X		ret = regnode(ANY);
X		*flagp |= HASWIDTH|SIMPLE;
X		break;
X	case '[': {
X			register int class;
X			register int classend;
X
X			if (*regparse == '^') {	/* Complement of range. */
X				ret = regnode(ANYBUT);
X				regparse++;
X			} else
X				ret = regnode(ANYOF);
X			if (*regparse == ']' || *regparse == '-')
X				regc(*regparse++);
X			while (*regparse != '\0' && *regparse != ']') {
X				if (*regparse == '-') {
X					regparse++;
X					if (*regparse == ']' || *regparse == '\0')
X						regc('-');
X					else {
X						class = UCHARAT(regparse-2)+1;
X						classend = UCHARAT(regparse);
X						if (class > classend+1)
X							FAIL("invalid [] range");
X						for (; class <= classend; class++)
X							regc(class);
X						regparse++;
X					}
X				} else
X					regc(*regparse++);
X			}
X			regc('\0');
X			if (*regparse != ']')
X				FAIL("unmatched []");
X			regparse++;
X			*flagp |= HASWIDTH|SIMPLE;
X		}
X		break;
X	case '(':
X		ret = reg(1, &flags);
X		if (ret == NULL)
X			return(NULL);
X		*flagp |= flags&(HASWIDTH|SPSTART);
X		break;
X	case '\0':
X	case '|':
X	case ')':
X		FAIL("internal urp");	/* Supposed to be caught earlier. */
X		break;
X	case '?':
X	case '+':
X	case '*':
X		FAIL("?+* follows nothing");
X		break;
X	case '\\':
X		if (*regparse == '\0')
X			FAIL("trailing \\");
X		ret = regnode(EXACTLY);
X		regc(*regparse++);
X		regc('\0');
X		*flagp |= HASWIDTH|SIMPLE;
X		break;
X	default: {
X			register int len;
X			register char ender;
X
X			regparse--;
X			len = strcspn(regparse, META);
X			if (len <= 0)
X				FAIL("internal disaster");
X			ender = *(regparse+len);
X			if (len > 1 && ISMULT(ender))
X				len--;		/* Back off clear of ?+* operand. */
X			*flagp |= HASWIDTH;
X			if (len == 1)
X				*flagp |= SIMPLE;
X			ret = regnode(EXACTLY);
X			while (len > 0) {
X				regc(*regparse++);
X				len--;
X			}
X			regc('\0');
X		}
X		break;
X	}
X
X	return(ret);
X}
X
X/*
X - regnode - emit a node
X */
Xstatic char *			/* Location. */
Xregnode(op)
Xchar op;
X{
X	register char *ret;
X	register char *ptr;
X
X	ret = regcode;
X	if (ret == &regdummy) {
X		regsize += 3;
X		return(ret);
X	}
X
X	ptr = ret;
X	*ptr++ = op;
X	*ptr++ = '\0';		/* Null "next" pointer. */
X	*ptr++ = '\0';
X	regcode = ptr;
X
X	return(ret);
X}
X
X/*
X - regc - emit (if appropriate) a byte of code
X */
Xstatic void
Xregc(b)
Xchar b;
X{
X	if (regcode != &regdummy)
X		*regcode++ = b;
X	else
X		regsize++;
X}
X
X/*
X - reginsert - insert an operator in front of already-emitted operand
X *
X * Means relocating the operand.
X */
Xstatic void
Xreginsert(op, opnd)
Xchar op;
Xchar *opnd;
X{
X	register char *src;
X	register char *dst;
X	register char *place;
X
X	if (regcode == &regdummy) {
X		regsize += 3;
X		return;
X	}
X
X	src = regcode;
X	regcode += 3;
X	dst = regcode;
X	while (src > opnd)
X		*--dst = *--src;
X
X	place = opnd;		/* Op node, where operand used to be. */
X	*place++ = op;
X	*place++ = '\0';
X	*place++ = '\0';
X}
X
X/*
X - regtail - set the next-pointer at the end of a node chain
X */
Xstatic void
Xregtail(p, val)
Xchar *p;
Xchar *val;
X{
X	register char *scan;
X	register char *temp;
X	register int offset;
X
X	if (p == &regdummy)
X		return;
X
X	/* Find last node. */
X	scan = p;
X	for (;;) {
X		temp = regnext(scan);
X		if (temp == NULL)
X			break;
X		scan = temp;
X	}
X
X	if (OP(scan) == BACK)
X		offset = scan - val;
X	else
X		offset = val - scan;
X	*(scan+1) = (offset>>8)&0377;
X	*(scan+2) = offset&0377;
X}
X
X/*
X - regoptail - regtail on operand of first argument; nop if operandless
X */
Xstatic void
Xregoptail(p, val)
Xchar *p;
Xchar *val;
X{
X	/* "Operandless" and "op != BRANCH" are synonymous in practice. */
X	if (p == NULL || p == &regdummy || OP(p) != BRANCH)
X		return;
X	regtail(OPERAND(p), val);
X}
X
X/*
X * regexec and friends
X */
X
X/*
X * Global work variables for regexec().
X */
Xstatic char *reginput;		/* String-input pointer. */
Xstatic char *regbol;		/* Beginning of input, for ^ check. */
Xstatic char **regstartp;	/* Pointer to startp array. */
Xstatic char **regendp;		/* Ditto for endp. */
X
X/*
X * Forwards.
X */
XSTATIC int regtry();
XSTATIC int regmatch();
XSTATIC int regrepeat();
X
X#ifdef DEBUG
Xint regnarrate = 0;
Xvoid regdump();
XSTATIC char *regprop();
X#endif
X
X/*
X - regexec - match a regexp against a string
X */
Xint
Xregexec(prog, string)
Xregister regexp *prog;
Xregister char *string;
X{
X	register char *s;
X	extern char *strchr();
X
X	/* Be paranoid... */
X	if (prog == NULL || string == NULL) {
X		regerror("NULL parameter");
X		return(0);
X	}
X
X	/* Check validity of program. */
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("corrupted program");
X		return(0);
X	}
X
X	/* If there is a "must appear" string, look for it. */
X	if (prog->regmust != NULL) {
X		s = string;
X		while ((s = strchr(s, prog->regmust[0])) != NULL) {
X			if (strncmp(s, prog->regmust, prog->regmlen) == 0)
X				break;	/* Found it. */
X			s++;
X		}
X		if (s == NULL)	/* Not present. */
X			return(0);
X	}
X
X	/* Mark beginning of line for ^ . */
X	regbol = string;
X
X	/* Simplest case:  anchored match need be tried only once. */
X	if (prog->reganch)
X		return(regtry(prog, string));
X
X	/* Messy cases:  unanchored match. */
X	s = string;
X	if (prog->regstart != '\0')
X		/* We know what char it must start with. */
X		while ((s = strchr(s, prog->regstart)) != NULL) {
X			if (regtry(prog, s))
X				return(1);
X			s++;
X		}
X	else
X		/* We don't -- general case. */
X		do {
X			if (regtry(prog, s))
X				return(1);
X		} while (*s++ != '\0');
X
X	/* Failure. */
X	return(0);
X}
X
X/*
X - regtry - try match at specific point
X */
Xstatic int			/* 0 failure, 1 success */
Xregtry(prog, string)
Xregexp *prog;
Xchar *string;
X{
X	register int i;
X	register char **sp;
X	register char **ep;
X
X	reginput = string;
X	regstartp = prog->startp;
X	regendp = prog->endp;
X
X	sp = prog->startp;
X	ep = prog->endp;
X	for (i = NSUBEXP; i > 0; i--) {
X		*sp++ = NULL;
X		*ep++ = NULL;
X	}
X	if (regmatch(prog->program + 1)) {
X		prog->startp[0] = string;
X		prog->endp[0] = reginput;
X		return(1);
X	} else
X		return(0);
X}
X
X/*
X - regmatch - main matching routine
X *
X * Conceptually the strategy is simple:  check to see whether the current
X * node matches, call self recursively to see whether the rest matches,
X * and then act accordingly.  In practice we make some effort to avoid
X * recursion, in particular by going through "ordinary" nodes (that don't
X * need to know whether the rest of the match failed) by a loop instead of
X * by recursion.
X */
Xstatic int			/* 0 failure, 1 success */
Xregmatch(prog)
Xchar *prog;
X{
X	register char *scan;	/* Current node. */
X	char *next;		/* Next node. */
X	extern char *strchr();
X
X	scan = prog;
X#ifdef DEBUG
X	if (scan != NULL && regnarrate)
X		fprintf(stderr, "%s(\n", regprop(scan));
X#endif
X	while (scan != NULL) {
X#ifdef DEBUG
X		if (regnarrate)
X			fprintf(stderr, "%s...\n", regprop(scan));
X#endif
X		next = regnext(scan);
X
X		switch (OP(scan)) {
X		case BOL:
X			if (reginput != regbol)
X				return(0);
X			break;
X		case EOL:
X			if (*reginput != '\0')
X				return(0);
X			break;
X		case ANY:
X			if (*reginput == '\0')
X				return(0);
X			reginput++;
X			break;
X		case EXACTLY: {
X				register int len;
X				register char *opnd;
X
X				opnd = OPERAND(scan);
X				/* Inline the first character, for speed. */
X				if (*opnd != *reginput)
X					return(0);
X				len = strlen(opnd);
X				if (len > 1 && strncmp(opnd, reginput, len) != 0)
X					return(0);
X				reginput += len;
X			}
X			break;
X		case ANYOF:
X			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
X				return(0);
X			reginput++;
X			break;
X		case ANYBUT:
X			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
X				return(0);
X			reginput++;
X			break;
X		case NOTHING:
X			break;
X		case BACK:
X			break;
X		case OPEN+1:
X		case OPEN+2:
X		case OPEN+3:
X		case OPEN+4:
X		case OPEN+5:
X		case OPEN+6:
X		case OPEN+7:
X		case OPEN+8:
X		case OPEN+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - OPEN;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set startp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regstartp[no] == NULL)
X						regstartp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case CLOSE+1:
X		case CLOSE+2:
X		case CLOSE+3:
X		case CLOSE+4:
X		case CLOSE+5:
X		case CLOSE+6:
X		case CLOSE+7:
X		case CLOSE+8:
X		case CLOSE+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - CLOSE;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set endp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regendp[no] == NULL)
X						regendp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case BRANCH: {
X				register char *save;
X
X				if (OP(next) != BRANCH)		/* No choice. */
X					next = OPERAND(scan);	/* Avoid recursion. */
X				else {
X					do {
X						save = reginput;
X						if (regmatch(OPERAND(scan)))
X							return(1);
X						reginput = save;
X						scan = regnext(scan);
X					} while (scan != NULL && OP(scan) == BRANCH);
X					return(0);
X					/* NOTREACHED */
X				}
X			}
X			break;
X		case STAR:
X		case PLUS: {
X				register char nextch;
X				register int no;
X				register char *save;
X				register int min;
X
X				/*
X				 * Lookahead to avoid useless match attempts
X				 * when we know what character comes next.
X				 */
X				nextch = '\0';
X				if (OP(next) == EXACTLY)
X					nextch = *OPERAND(next);
X				min = (OP(scan) == STAR) ? 0 : 1;
X				save = reginput;
X				no = regrepeat(OPERAND(scan));
X				while (no >= min) {
X					/* If it could work, try it. */
X					if (nextch == '\0' || *reginput == nextch)
X						if (regmatch(next))
X							return(1);
X					/* Couldn't or didn't -- back up. */
X					no--;
X					reginput = save + no;
X				}
X				return(0);
X			}
X			break;
X		case END:
X			return(1);	/* Success! */
X			break;
X		default:
X			regerror("memory corruption");
X			return(0);
X			break;
X		}
X
X		scan = next;
X	}
X
X	/*
X	 * We get here only if there's trouble -- normally "case END" is
X	 * the terminating point.
X	 */
X	regerror("corrupted pointers");
X	return(0);
X}
X
X/*
X - regrepeat - repeatedly match something simple, report how many
X */
Xstatic int
Xregrepeat(p)
Xchar *p;
X{
X	register int count = 0;
X	register char *scan;
X	register char *opnd;
X
X	scan = reginput;
X	opnd = OPERAND(p);
X	switch (OP(p)) {
X	case ANY:
X		count = strlen(scan);
X		scan += count;
X		break;
X	case EXACTLY:
X		while (*opnd == *scan) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYOF:
X		while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYBUT:
X		while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	default:		/* Oh dear.  Called inappropriately. */
X		regerror("internal foulup");
X		count = 0;	/* Best compromise. */
X		break;
X	}
X	reginput = scan;
X
X	return(count);
X}
X
X/*
X - regnext - dig the "next" pointer out of a node
X */
Xstatic char *
Xregnext(p)
Xregister char *p;
X{
X	register int offset;
X
X	if (p == &regdummy)
X		return(NULL);
X
X	offset = NEXT(p);
X	if (offset == 0)
X		return(NULL);
X
X	if (OP(p) == BACK)
X		return(p-offset);
X	else
X		return(p+offset);
X}
X
X#ifdef DEBUG
X
XSTATIC char *regprop();
X
X/*
X - regdump - dump a regexp onto stdout in vaguely comprehensible form
X */
Xvoid
Xregdump(r)
Xregexp *r;
X{
X	register char *s;
X	register char op = EXACTLY;	/* Arbitrary non-END op. */
X	register char *next;
X	extern char *strchr();
X
X
X	s = r->program + 1;
X	while (op != END) {	/* While that wasn't END last time... */
X		op = OP(s);
X		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */
X		next = regnext(s);
X		if (next == NULL)		/* Next ptr. */
X			printf("(0)");
X		else 
X			printf("(%d)", (s-r->program)+(next-s));
X		s += 3;
X		if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
X			/* Literal string, where present. */
X			while (*s != '\0') {
X				putchar(*s);
X				s++;
X			}
X			s++;
X		}
X		putchar('\n');
X	}
X
X	/* Header fields of interest. */
X	if (r->regstart != '\0')
X		printf("start `%c' ", r->regstart);
X	if (r->reganch)
X		printf("anchored ");
X	if (r->regmust != NULL)
X		printf("must have \"%s\"", r->regmust);
X	printf("\n");
X}
X
X/*
X - regprop - printable representation of opcode
X */
Xstatic char *
Xregprop(op)
Xchar *op;
X{
X	register char *p;
X	static char buf[50];
X
X	(void) strcpy(buf, ":");
X
X	switch (OP(op)) {
X	case BOL:
X		p = "BOL";
X		break;
X	case EOL:
X		p = "EOL";
X		break;
X	case ANY:
X		p = "ANY";
X		break;
X	case ANYOF:
X		p = "ANYOF";
X		break;
X	case ANYBUT:
X		p = "ANYBUT";
X		break;
X	case BRANCH:
X		p = "BRANCH";
X		break;
X	case EXACTLY:
X		p = "EXACTLY";
X		break;
X	case NOTHING:
X		p = "NOTHING";
X		break;
X	case BACK:
X		p = "BACK";
X		break;
X	case END:
X		p = "END";
X		break;
X	case OPEN+1:
X	case OPEN+2:
X	case OPEN+3:
X	case OPEN+4:
X	case OPEN+5:
X	case OPEN+6:
X	case OPEN+7:
X	case OPEN+8:
X	case OPEN+9:
X		sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
X		p = NULL;
X		break;
X	case CLOSE+1:
X	case CLOSE+2:
X	case CLOSE+3:
X	case CLOSE+4:
X	case CLOSE+5:
X	case CLOSE+6:
X	case CLOSE+7:
X	case CLOSE+8:
X	case CLOSE+9:
X		sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
X		p = NULL;
X		break;
X	case STAR:
X		p = "STAR";
X		break;
X	case PLUS:
X		p = "PLUS";
X		break;
X	default:
X		regerror("corrupted opcode");
X		break;
X	}
X	if (p != NULL)
X		(void) strcat(buf, p);
X	return(buf);
X}
X#endif
X
X/*
X * The following is provided for those people who do not have strcspn() in
X * their C libraries.  They should get off their butts and do something
X * about it; at least one public-domain implementation of those (highly
X * useful) string routines has been published on Usenet.
X */
X#ifdef STRCSPN
X/*
X * strcspn - find length of initial segment of s1 consisting entirely
X * of characters not from s2
X */
X
Xstatic int
Xstrcspn(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X	register char *scan1;
X	register char *scan2;
X	register int count;
X
X	count = 0;
X	for (scan1 = s1; *scan1 != '\0'; scan1++) {
X		for (scan2 = s2; *scan2 != '\0';)	/* ++ moved down. */
X			if (*scan1 == *scan2++)
X				return(count);
X		count++;
X	}
X	return(count);
X}
X#endif
*-*-END-of-src/regexp.c-*-*
echo x - src/regsub.c
sed 's/^X//' >src/regsub.c <<'*-*-END-of-src/regsub.c-*-*'
X/*
X * regsub
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X */
X#include <stdio.h>
X#include "regexp.h"
X#include "regmagic.h"
X
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X/*
X - regsub - perform substitutions after a regexp match
X */
Xvoid
Xregsub(prog, source, dest)
Xregexp *prog;
Xchar *source;
Xchar *dest;
X{
X	register char *src;
X	register char *dst;
X	register char c;
X	register int no;
X	register int len;
X	extern char *strncpy();
X
X	if (prog == NULL || source == NULL || dest == NULL) {
X		regerror("NULL parm to regsub");
X		return;
X	}
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("damaged regexp fed to regsub");
X		return;
X	}
X
X	src = source;
X	dst = dest;
X	while ((c = *src++) != '\0') {
X		if (c == '&')
X			no = 0;
X		else if (c == '\\' && '0' <= *src && *src <= '9')
X			no = *src++ - '0';
X		else
X			no = -1;
X
X		if (no < 0) {	/* Ordinary character. */
X			if (c == '\\' && (*src == '\\' || *src == '&'))
X				c = *src++;
X			*dst++ = c;
X		}
X		else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
X			len = prog->endp[no] - prog->startp[no];
X			(void) strncpy(dst, prog->startp[no], len);
X			dst += len;
X			if (len != 0 && *(dst-1) == '\0') {		/* strncpy hit NUL. */
X				regerror("damaged match string");
X				return;
X			}
X		}
X	}
X	*dst++ = '\0';
X}
*-*-END-of-src/regsub.c-*-*
echo x - src/rev.c
sed 's/^X//' >src/rev.c <<'*-*-END-of-src/rev.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |			   REVERSE ORDER OF INPUT			     |
X  |									     |
X  |				Version 1.0				     |
X  |									     |
X  |                (or, when Computer Science gets to you)                   |
X  |									     |
X  |		       Written by Anastasios Kotsikonas                      |
X  |		    	      (tasos@cs.bu.edu)			     	     |
X  |	                                                                     |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X
Xmain ()
X{
X  char linein[65536];
X  char lineout[65536];
X  long int i, j;
X
X  while (!feof (stdin)) {
X    linein[0] = lineout[0] = '\0';
X    fgets (linein, 65535, stdin);
X    if (linein [(j = strlen (linein)) - 1] == '\n')
X      lineout [j - 1]= '\n',
X      --j;
X    for (i = j - 1, j = 0; i >= 0; i--)
X      lineout[j++] = linein[i];
X    if (lineout[j] == '\n') j++;
X    lineout[j] = '\0';
X    fputs (lineout, stdout);
X  }
X  exit (0);
X}
*-*-END-of-src/rev.c-*-*
echo x - src/sem.c
sed 's/^X//' >src/sem.c <<'*-*-END-of-src/sem.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |				SEMAPHORE OPERATIONS			     |
X  |									     |
X  |				     version 1.0			     |
X  |									     |
X  | User contributed code.						     |
X  | Author: Thimios Panagos <thimios@cs.bu.edu>				     |
X  ----------------------------------------------------------------------------
X*/
X
X#include <signal.h>
X#include "defs.h"
X#ifdef GO_INTERACTIVE
X# include <stdio.h>
X# include <sys/types.h>
X# include <sys/ipc.h>
X# include <sys/sem.h>
X
Xint semtran (int);
Xint semcall (int, int);
Xint P (int);
Xint V (int);
Xvoid semremove (int);
X
X/*
X  Create a semaphore. Return the semaphore id, or -1 on error.
X*/
X
Xint semtran (int key) 
X{
X  int sid;
X
X  if ((sid = semget ((key_t) key, 1, 0700 | IPC_CREAT | SEM_UNDO)) < 0)
X    return -1;
X  if (V (sid))          /* Initialize semaphore value to 1 */
X    return -1;
X  return sid;
X}
X
X/*
X  Operate on a semaphore (increment or decrement).
X*/
X
Xint semcall (int sid, int op)
X{
X  struct sembuf sb;
X
X  sb.sem_num = sb.sem_flg = 0;
X  sb.sem_op = op;
X
X  if (semop (sid, &sb, 1) == -1)
X    return -1;
X  return 0;
X}
X
X/*
X  P semaphore; call when attempting to get into critical section.
X*/
X
Xint P (int sid)     
X{
X  if (semcall (sid, -1))
X    return -1;
X  return 0;
X}
X
X/*
X  V semaphore; call when out of critical section.
X*/
X
Xint V (int sid)  
X{
X  if (semcall (sid, 1))
X    return -1;
X  return 0;
X}
X
X/*
X  Destroy a semaphore.
X*/
X
Xvoid semremove (int sid)
X{
X  (void) semctl (sid, 0, IPC_RMID, 0); 
X}
X#endif
*-*-END-of-src/sem.c-*-*
echo x - src/sender.c
sed 's/^X//' >src/sender.c <<'*-*-END-of-src/sender.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                	  SENDER ORIENTED FUNCTIONS			     |
X  |                                                                          |
X  |                              Version 2.3                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <string.h>
X#include <ctype.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include "defs.h"
X
X#define	_SUBSCRIBERS	1
X#define	_ALIASES	2
X#define	OTHER		3
X
X#ifdef __STDC__
Xextern  char *tsprintf (char *, ...);
X#else
Xextern  char *tsprintf ();
X#endif
Xextern  char **alternate_addresses;
X
Xextern  void report_progress (FILE *, char *, int);
Xextern  char *upcase (char *);
Xextern  int  gexit (int);
Xextern  int  re_strcmp (char *, char *, char *);
X
XBOOLEAN subscribed (FILE *, char *, char *, char *, char *, char *);
XBOOLEAN check_file (FILE *, char *, char *, BOOLEAN);
XBOOLEAN ignore_sender (FILE *, char *, FILE *, BOOLEAN);
XBOOLEAN owner_listed (char *, char *, char *, FILE *);
XBOOLEAN host_listed (char *, char *, FILE *);
Xvoid    check_aliases (char *, char *);
XBOOLEAN extract_sender (char *);
XBOOLEAN extract_subscriber (FILE *, char *, BOOLEAN);
Xvoid    extract_origin (char *);
Xvoid    extract_address (char *);
Xvoid	escape_address (char *);
X
X/*
X  Check if 'sender' is subscribed. Return SUBSCRIBED, NEWS, PEER or
X  NOTSUBSCRIBED. Comparisons are done in upper case.
X*/
X
XBOOLEAN subscribed (FILE *report, char *sender, char *Subscribersf,
X		    char *Newsf, char *Peersf, char *Aliasesf)
X{
X  if (!Subscribersf) /* call made by distribute () in listserv.c */
X    return NOTSUBSCRIBED;
X  alternate_addresses = NULL;
X  if (check_file (report, sender, Subscribersf, _SUBSCRIBERS) ||
X      (check_file (report, sender, Aliasesf, _ALIASES) &&
X       check_file (report, sender, Subscribersf, _SUBSCRIBERS)))
X    return SUBSCRIBED;
X  else if (Newsf &&
X	   (check_file (report, sender, Newsf, OTHER) ||
X	    (check_file (report, sender, Aliasesf, _ALIASES) &&
X	     check_file (report, sender, Newsf, OTHER))))
X    return NEWS;
X  else if (Peersf &&
X	   (check_file (report, sender, Peersf, OTHER) ||
X	    (check_file (report, sender, Aliasesf, _ALIASES) &&
X	     check_file (report, sender, Peersf, OTHER))))
X    return PEER;
X  return NOTSUBSCRIBED;
X}
X
X/*
X  Check the given file for subscription. If the file is an aliases file
X  and the sender is listed in there, store his actual mailing address
X  in 'sender'.
X*/
X
XBOOLEAN check_file (FILE *report, char *sender, char *file,
X		    BOOLEAN which_file)
X{
X  char subscriber [MAX_LINE];
X  char sender_copy [MAX_LINE];
X  char mode [MAX_LINE];
X  char othermode [MAX_LINE];
X  char name [MAX_LINE];
X  char *at, *dot;
X  FILE *f;
X  int i, nmatches = 0;
X  extern char password_in_sub_file [];
X
X  if (file == NULL || *sender == EOS) /* serverd does not check the aliases files */
X    return NOTSUBSCRIBED;
X  OPEN_FILE (f, file, "r", "check_file");
X  upcase (sender);          /* Convert to upper case */
X  while (!feof (f)) {  /* Check list of subscribers/peers/news/aliases */
X    subscriber[0] = mode[0] = othermode[0] = RESET (name);
X    extract_subscriber (f, subscriber, (which_file == _ALIASES ? TRUE : FALSE));
X    if (subscriber[0] != EOS) {
X      fscanf (f, "%s ", mode);
X      if (which_file == _SUBSCRIBERS) /* Only this file may have more options */
X        for (i = 1; i < MAX_SET_OPTIONS - 1; i++) {
X          fscanf (f, "%s ", othermode);
X	  if (i == 1) /* Save user's password */
X	    strcpy (password_in_sub_file, othermode);
X        }
X      if (which_file != _ALIASES)
X        fgets (name, MAX_LINE - 2, f);
X      upcase (subscriber);
X      if ((which_file == _ALIASES && re_strcmp (subscriber, sender, mode) > 0)
X	  || (which_file != _ALIASES && !strcmp (subscriber, sender))) {
X        fclose (f);
X        if (which_file == _ALIASES)
X	  upcase (mode),
X	  strcpy (sender, mode); /* Note: mode now is address-as-subscribed */
X        return SUBSCRIBED;
X      }
X      strcpy (sender_copy, sender);
X      if ((at = strchr (sender_copy, '@')) && (dot = strchr (at, '.'))) {
X	sprintf (at + 1, ".*%s", dot + 1);
X	if (re_strcmp (sender_copy, subscriber, NULL) > 0) {
X	  if (!alternate_addresses) {
X	    if ((alternate_addresses = (char **) malloc (2 * sizeof (char *)))
X		== NULL)
X	      report_progress (report, "\ncheck_file(): malloc() failed", TRUE),
X	      gexit (11);
X	  }
X	  else
X	    if ((alternate_addresses = (char **)
X		 realloc ((char **) alternate_addresses,
X			  sizeof (char *) * (nmatches + 2))) == NULL)
X	      report_progress (report, "\ncheck_file(): realloc() failed", TRUE),
X	      gexit (11);
X	  alternate_addresses [nmatches + 1] = NULL;
X	  alternate_addresses [nmatches] = (char *)
X	    malloc ((strlen (subscriber) + 1) * sizeof (char));
X	  strcpy (alternate_addresses [nmatches++], subscriber);
X	}
X      }
X    }
X  }
X  fclose (f);
X  return NOTSUBSCRIBED;
X}
X
X/*
X  Check if 'sender' appears in the IGNORED file or MESSAGE_IDS.
X*/
X
XBOOLEAN ignore_sender (FILE *ignored, char *sender, FILE *report,
X		       BOOLEAN literal_match)
X{
X  char ignored_user [MAX_LINE];
X  char line[MAX_LINE];
X
X  rewind (ignored);
X  while (!feof (ignored)) { /* Check the IGNORED file first */
X    line[0] = RESET (ignored_user);
X    fgets (line, MAX_LINE - 2, ignored);
X    sscanf (line, "%s", ignored_user);
X    upcase (ignored_user);
X    if (ignored_user[0] != EOS) {
X      if (!literal_match) {
X	if (re_strcmp (ignored_user, sender, NULL) > 0) {
X	  report_progress (report, tsprintf ("User %s and message ignored.\n",
X					     sender), FALSE);
X	  return TRUE;
X	}
X      }
X      else if (!strcmp (ignored_user, sender)) {
X	report_progress (report, tsprintf ("User %s and message ignored.\n",
X					   sender), FALSE);
X	return TRUE;
X      }
X    }
X  }
X  return FALSE;
X}
X
X/*
X  Check whether the list 'owner' is listed.
X*/
X
XBOOLEAN owner_listed (char *owners, char *owner, char *list, FILE *report)
X{
X  char registered_owner [MAX_LINE];
X  char assigned_list [MAX_LINE];
X  char line [MAX_LINE];
X  FILE *f;
X
X  OPEN_FILE (f, owners, "r", "owner_listed");
X  while (!feof (f)) {
X    line[0] = assigned_list[0] = RESET (registered_owner);
X    fgets (line, MAX_LINE - 2, f);
X    sscanf (line, "%s %s", registered_owner, assigned_list);
X    upcase (registered_owner);
X    upcase (assigned_list);
X    if (registered_owner[0] != EOS)
X      if (!strcmp (registered_owner, owner) && !strcmp (assigned_list, list)) {
X	fclose (f);
X	return OWNER;
X      }
X  }
X  fclose (f);
X  return FALSE;
X}
X
X/*
X  Check whether the given host appears in a hosts file.
X*/
X
XBOOLEAN host_listed (char *hosts, char *host, FILE *report)
X{
X  char _host [MAX_LINE];
X  FILE* f;
X
X  OPEN_FILE (f, hosts, "r", "host_listed");
X  while (!feof (f)) {
X    RESET (_host);
X    fscanf (f, "%s", _host);
X    upcase (_host);
X    if (_host[0] == '#') {
X      fgets (_host, MAX_LINE - 2, f);
X      continue;
X    }
X    if (_host[0] != EOS && re_strcmp (_host, host, NULL) > 0) {
X      fclose (f);
X      return TRUE;
X    }
X  }
X  fclose (f);
X  return FALSE;
X}
X
X/*
X  Look up 'sender' in the 'aliases' file. If a match is found, use
X  the alternate address.
X*/
X
Xvoid check_aliases (char *aliases, char *sender)
X{
X  char line [MAX_LINE];
X  char newalias [MAX_LINE];
X  char alias [MAX_LINE];
X  FILE *f;
X
X  if ((f = fopen (aliases, "r")) == NULL)
X    return;
X  upcase (sender);
X  while (!feof (f)) {
X    line[0] = alias[0] = RESET (newalias);
X    fgets (line, MAX_LINE - 2, f);
X    sscanf (line, "%s %s", alias, newalias);
X    upcase (alias);
X    if (alias[0] != EOS)
X      if (re_strcmp (alias, sender, newalias) > 0)
X	upcase (newalias),
X	strcpy (sender, newalias);
X  }
X  fclose (f);
X  return;
X}
X
X/*
X  Extract the sender's email address. To do that, skip over the leading
X  "From ". That's where the address starts. Then put an EOS character at
X  the end of this address (a blank terminates this address string).
X  Addresses that contain single quotes are treated as invalid (contrary to
X  RFC 822 specs) since they create problems with the shell.
X*/
X
XBOOLEAN extract_sender (char *s)
X{
X  int nsquote = 0, ndquote = 0, nparen = 0, nangle = 0, nsquare = 0;
X  BOOLEAN done = FALSE, invalid_char = FALSE;
X  char *r = s;
X#ifdef DEBUG
X  char *p = s + strlen (s);
X#endif
X
X  if (strncmp (s, START_OF_MESSAGE, strlen (START_OF_MESSAGE))) {
X    s [strlen (START_OF_MESSAGE)] = EOS;
X    return FALSE;
X  }
X  sprintf (s, "%s", s + strlen (START_OF_MESSAGE));
X  while (*s != EOS && !done) { /* Look for end of address substring */
X    (*s == '`' || *s == '*' || *s == '?' || *s == '|' ? invalid_char = TRUE : 0);
X/*
X    (*s == '/' && (s == r || *(s - 1) == ';') ? invalid_char = TRUE : 0);
X*/
X    (*s == '\'' ? (nsquote = 1) : 0);
X    (*s == '\"' ? (s != r ? (*(s - 1) != '\\' ? (ndquote = !ndquote) : 0) :
X		   (ndquote = !ndquote)) : 0);
X    (*s == '(' ? (!ndquote ? ++nparen : 0) : 0);
X    (*s == ')' ? (!ndquote ? --nparen : 0) : 0);
X    (*s == '<' ? (!ndquote ? ++nangle : 0) : 0);
X    (*s == '>' ? (!ndquote ? --nangle : 0) : 0);
X    (*s == '[' ? (!ndquote ? ++nsquare : 0): 0);
X    (*s == ']' ? (!ndquote ? --nsquare : 0) : 0);
X    (isspace (*s) ?
X     ((ndquote || nparen || nangle || nsquare) ? 0 : (done = TRUE)) : 0);
X    ++s;
X#ifdef DEBUG
X    if (s > p) { /* Beyond EOS */
X      extern FILE *report;
X      report_progress (report, "extract_sender(): memory being overwritten",
X                       TRUE);
X    }
X#endif
X  }
X  *(s - 1) = EOS;  /* 's' is now pointing to the sender's address */
X  if (nsquote || ndquote || nparen || nangle || nsquare || invalid_char)
X    return FALSE;	/* Error in address */
X  return TRUE;
X}
X
X/*
X  Extract the subscriber's address from the file. Addresses that contain
X  single quotes are treated as invalid (contrary to RFC 822 specs) since they
X  create problems with the shell.
X*/
X
XBOOLEAN extract_subscriber (FILE *f, char *subscriber, BOOLEAN aliases_file)
X{
X  int ndquote = 0, nparen = 0, nangle = 0, nsquare = 0, i = 0;
X  int nsquote = 0;
X  BOOLEAN done = FALSE, invalid_char = FALSE;
X  char c = EOS, pc;
X
X  while (!feof (f) && !done && i < MAX_LINE) {
X    pc = c;
X    c = fgetc (f);
X    (c == '`' || c == '*' || c == '?' || c == '|' ? invalid_char = TRUE : 0);
X/*
X    (c == '/' && (pc == EOS || pc == ';') ? invalid_char = TRUE : 0);
X*/
X    (c == '\'' ? (nsquote = 1) : 0);
X    (c == '\"' ? (pc != '\\' ? (ndquote = !ndquote) : 0) : 0);
X    (c == '(' ? (!ndquote ? ++nparen : 0) : 0);
X    (c == ')' ? (!ndquote ? --nparen : 0) : 0);
X    (c == '<' ? (!ndquote ? ++nangle : 0) : 0);
X    (c == '>' ? (!ndquote ? --nangle : 0) : 0);
X    (c == '[' ? (!ndquote ? ++nsquare : 0): 0);
X    (c == ']' ? (!ndquote ? --nsquare : 0) : 0);
X    (isspace (c) ?
X     ((!aliases_file && (ndquote || nparen || nangle || nsquare)) ? 0 
X      : (done = TRUE)) : 0);
X    subscriber [i++] = c;
X  }
X  if (i > 0)
X    subscriber [i - 1] = EOS;
X  if (!aliases_file &&
X      (nsquote || ndquote || nparen || nangle || nsquare || invalid_char))
X    return FALSE;	/* Error in address */
X  return TRUE;
X}
X
X/*
X  Extract the originator of the message, i.e. the list that the original
X  message first originated from.
X*/
X
Xvoid extract_origin (char *s)
X{
X  char *p;
X
X  if (p = strchr (s, '<'))
X    sprintf (s, "%s", p + 1); /* Remove '<' */
X  if (p = strchr (s, '>'))
X    *p = EOS;  /* Remove '>' */
X  else {  /* Get to the end of the address */
X    while (*s != EOS && !isspace (*s))
X      ++s;
X    *s = EOS;
X  }
X}
X
X/*
X  Given an RFC 822 From: line address, strip comments etc. and extract
X  the actual address.
X*/
X
Xvoid extract_address (char *s)
X{
X  char *p;
X
X  if (p = strchr (s, '<'))
X    sprintf (s, "%s", p + 1); /* Remove '<' */
X  if (*s == '|' || *s == ':')
X    *s = EOS;  /* Protect against trojans */
X  if (p = strchr (s, '>'))
X    *p = EOS;  /* Remove '>' */
X  if (p = strchr (s, '('))
X    *p = EOS; /* Remove comments */
X  else {  /* Get to the end of the address */
X    while (*s != EOS && !isspace (*s))
X      ++s;
X    *s = EOS;
X  }
X}
X
X/*
X  Scan 's' and escape the following characters:
X  ( ) [ ] < > { } \ : ; ' ` . ^ $ *
X*/
X
Xvoid escape_address (char *s)
X{
X  char *r;
X
X  while (*s != EOS) {
X    switch (*s) {
X    case '(': case ')': case '[': case ']': case '<': case '>': case '\\':
X    case ':': case ';': case '\'': case '`': case '.': case '^':
X    case '$': case '*': case '{': case '}':
X      r = s + strlen (s);	/* Start from the end */
X      while (r != s)
X        *(r + 1) = *r,
X	--r;
X      *(r + 1) = *r;
X      *r = '\\';
X      ++s;
X      break;
X    }
X    ++s;
X  }
X}
*-*-END-of-src/sender.c-*-*
echo x - src/serverd.c
sed 's/^X//' >src/serverd.c <<'*-*-END-of-src/serverd.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                      UNIX LISTSERVER SYSTEM DAEMON			     |
X  |                                                                          |
X  |                               Version 5.0                                |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.		     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  |                                                                          |
X  | Enhanced by: Warren Burstein.                                            |
X  ----------------------------------------------------------------------------
X
X  Spawn list or listserv upon arrival of new messages. Send mail to
X  MANAGER if something went wrong (only if using UCB mail).
X  serverd will not spawn if the current load average is above max_load
X  and the -l flag is on, until it has delayed MAX_TRIES * 30 seconds.
X  serverd communicates with listerv via exit statuses 6 and 7; these are
X  the requests to shutdown/restart the system; on status 7, serverd spawns
X  start and dies; if it cannot restart, it sends mail and commits suicide.
X  serverd reports to REPORT_SERVERD.
X
X  The server may also run in interactive mode and listen for TCP connections
X  at a specified port. Users are able of entering requests and getting replies
X  live. For this, a separate listener process is spawned which accepts or
X  rejects the connections, then it in turn forks off children to handle
X  accepted connections.
X
X		Master process	(looks for email requests/messages)
X		       |
X		    Listener	(accepts or rejects connection requests)
X		       |
X		      /|\
X		    children	(process live requests)
X
X  If the Master process dies in any way but a SIGKILL signal, then all
X  processes abort. If the Listener process dies (including a SIGKILL signal),
X  all processes abort as well. If the Master is killed with SIGKILL, behavior
X  will be undefined and a semaphore will be left unused in the system (use
X  ipcrm to remove it). If any of the children die abnormally (including a
X  SIGKILL signal) the Listener process will report to that effect; however,
X  the connection will remain "open" on the other end until it times out
X  locally.
X
X  A "restart" or "shutdown" request (live or not) will kill all processes and
X  in the case of "restart", a new Master and Listener will be created.
X
X  Synchronization between all processes is done via a semaphore. Removing or
X  altering the semaphore while the system is running will bring the system
X  down.
X
X  COMMAND LINE OPTIONS:
X    -1: Execute only once -- to be used with cron.
X    -l: Enforce restrictions based on the current load average.
X    -e: Echo reports to the screen.
X    -i: Interactive server; it listens for tcp connections. The argument
X	that follows is the maximum duration (in seconds) of each connection.
X
X  EXIT CODES:
X    0: OK
X    1: Could not open or lock file
X    2: SIGINT signal
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X    9: Too many multiple recipients
X   10: Could not deliver mail
X   11: Malloc failed
X   12: Cannot fork
X   13: Socket connection problem
X   14: Semaphore error
X   15: Cannot setuid, setgid
X   16: Internal error
X
X*/
X
X#include <stdio.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/types.h>
X#if defined (stardent) || defined (stellar) || defined (titan)
X# include <rpc/types.h>
X#endif
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#include <sys/stat.h>
X#include <signal.h>
X#include <fcntl.h>
X#include <time.h>
X#include <ctype.h>
X#include <string.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <math.h>
X#include <sys/wait.h>
X#if !defined (sequent) && !defined (__NeXT__) && !defined (__convex__) && \
X !defined (apollo) && !defined (i386) && !defined (unknown_port)
X# include <sys/termio.h>
X#endif
X#ifndef sun
X# include <sys/ioctl.h>
X#endif
X#include "defs.h"
X#include "serverd.h"
X#include "struct.h"
X#include "global.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X#ifdef GO_INTERACTIVE
X# include <sys/socket.h>
X# ifndef stellar
X#  include <sys/time.h>
X# endif
X# include <netinet/in.h>
X# include <netdb.h>
X# include <sys/ipc.h>
X# include <sys/sem.h>
X# include <pwd.h>
X#endif
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xextern int  syscom ();
Xextern char *tsprintf ();
X#endif
X
X#if !defined (__NeXT__) && !defined (__osf__) && !defined (_AIX)
Xextern double atof ();
Xextern long int atoi (char *);
X#else
Xextern int atoi (const char *);
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern int  _getopt (int, char **, char *);
Xextern void setup_string (char *, char *, char *);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern void report_progress (FILE *, char *, int);
Xextern int  lock_file (char *, int, int, BOOLEAN);
Xextern void unlock_file (int);
Xextern void clean_request (char *);
Xextern char *upcase (char *);
Xextern BOOLEAN owner_listed (char *, char *, char *, FILE *);
Xextern BOOLEAN host_listed (char *, char *, FILE *);
Xextern BOOLEAN subscribed (FILE *, char *, char *, char *, char *, char *);
Xextern int long write_to_fd (int, char *, long int);
Xextern int otoi (char *);
Xextern char *_strstr (char *, char *);
Xextern void my_abort (int);
Xextern int echo (char *, char *);
Xextern int cat_append (char *, char *);
Xextern int re_strcmp (char *, char *, char *);
Xextern int insert_word (char *, char **, int, int, int);
Xextern char *mystrdup (char *);
Xextern int semtran (int);
Xextern int P (int);
Xextern int V (int);
Xextern void semremove (int);
X
X
Xvoid   main (int, char **, char **);
Xvoid   serverd_config (void);
Xvoid   usage (void);
Xint    gexit (int);
Xvoid   load_wait (float);
Xvoid   run_program (char *, BOOLEAN, int);
X#ifdef GO_INTERACTIVE
Xvoid   sighandle (int);
Xvoid   listener_exited (void);
Xvoid   close_connection (int);
Xint    check_timeout (CLIENT []);
Xint    create_connection (void);
Xvoid   process_live_request (int, struct sockaddr_in);
XBOOLEAN check_for_redirection (char *, char *, int *, int, BOOLEAN, BOOLEAN);
XBOOLEAN writeable_file (char *, int, BOOLEAN);
XBOOLEAN child_alive (int, CLIENT []);
X#endif
X
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  int rlfd = 2;
X  FILE *f, *fp;
X  float max_load;
X  BOOLEAN loadr = FALSE, execute_once = FALSE;
X  char list [MAX_LINE];
X  char server [MAX_LINE];
X  char msg [4096];
X  char *mask;
X  int c;
X#ifdef GO_INTERACTIVE
X  char *options = "1l:i:e", *s, reply [MAX_LINE], version [MAX_LINE], *ch;
X  char welcome [65536];
X  int connid = 0, port, l, pid, _nforks;
X  struct sockaddr_in client;
X  struct hostent *hp;
X  union semun {
X    int val;
X    struct semid_ds *buf;
X    ushort *array;
X  } sembuf;
X  int smask;
X#else
X  char *options = "1l:e";
X#endif
X  long int time_is = 0, current_date = -1, new_date, time_then = 0;
X  struct tm *t;
X  extern char *optarg, *getenv();
X  extern int optopt;
X  int i;
X
X  prog = argv[0];
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case '1': execute_once = TRUE; break;
X      case 'e': tty_echo = TRUE; break;
X      case 'l': loadr = TRUE; 
X		max_load = (float) atof (optarg); 
X		break;
X#ifdef GO_INTERACTIVE
X      case 'i':
X	interactive = TRUE;
X	if ((conn_duration = atoi (optarg)) <= 0)
X	  fprintf (stderr, "-i %d ???\n", conn_duration),
X	  exit (3);
X	break;
X#endif
X      case ':': 
X        fprintf (stderr, "serverd:  Option '%c' requires an argument.\n",
X		 optopt);
X	exit (3);
X      case '?':
X      default:
X	usage ();
X    }
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X#ifndef NO_LOCKS
X  if ((lfd = lock_file (SERVERD_LOCK_FILE, O_RDWR, 0, FALSE)) < 0)
X    fprintf (stderr, "serverd: Unable to lock %s. Aborting.\n", 
X             SERVERD_LOCK_FILE),
X    exit (2);
X#endif
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  if ((report = fopen (REPORT_SERVERD, "a")) == NULL)
X    fprintf (stderr, "serverd: Could not open %s\n", REPORT_SERVERD),
X    exit (1);
X  chmod (REPORT_SERVERD, 384);
X  fprintf (report, "%s", COPYRIGHT);
X  fflush (report);
X#endif
X
X#ifdef GO_INTERACTIVE
X  if ((pwentry = getpwuid (getuid ())) == NULL)
X    report_progress (report, "main(): Invalid user account", TRUE),
X    exit (3);
X# ifndef SYSLOG
X  if (chown (REPORT_SERVERD, pwentry->pw_uid, pwentry->pw_gid))
X    report_progress (report, "\nmain(): Cannot chown()", TRUE),
X    exit (15);
X# endif
X#endif
X  if ((f = fopen (PID_SERVERD, "w")) != NULL)
X    fprintf (f, "%d", getpid()),
X    fclose (f)
X#ifdef GO_INTERACTIVE
X    ,chown (PID_SERVERD, pwentry->pw_uid, pwentry->pw_gid);
X#else
X    ;
X#endif
X
X  signal (SIGINT, (void (*)()) gexit);
X#ifdef GO_INTERACTIVE
X# if defined (bsd)
X  sigsetmask (0);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X  sigrelse (SIGCLD);
X#  else
X  sigrelse (SIGCHLD);
X#  endif
X# endif
X# ifdef SIGCLD
X  signal (SIGCLD, (void (*)()) sighandle);
X# else
X  signal (SIGCHLD, (void (*)()) sighandle);
X# endif
X  signal (ABORT_SIG, (void (*)()) listener_exited);
X#endif
X  init_signals();
X  catch_signals ();
X
X  time (&time_is);
X
X  nlists = sys_config (report, &sys);
X  if (sys.fax.prog[0] == EOS)
X    strcat (sys.server.cmdoptions, " -d fax");
X#if defined (TIOCNOTTY) && defined (SIGTTOU) && defined (SIGTTIN)
X  if (!tty_echo)
X    if ((i = open ("/dev/tty", 2)) >=0)
X      ioctl (i, TIOCNOTTY, 0),
X      close (i);
X#endif
X
X  ppid = getpid ();
X#ifdef GO_INTERACTIVE
X  if (interactive) { /* Spawn Listener process */
X    if ((sid = semtran (0)) < 0)	/* Get semaphore id */
X      report_progress (report, "\nmain(): Cannot get semaphore", TRUE),
X      exit (14);
X    if ((sembuf.buf = (struct semid_ds *) malloc (sizeof (struct semid_ds))) ==
X	NULL)
X      report_progress (report, "\nmain(): malloc() failed", TRUE),
X      exit (11);
X    sembuf.buf->sem_perm.uid = pwentry->pw_uid;
X    sembuf.buf->sem_perm.gid = pwentry->pw_gid;
X    sembuf.buf->sem_perm.mode = 0700;
X    if (semctl (sid, 1, IPC_SET, sembuf)) /* Change owner to server */
X      report_progress (report, "\nmain(): Cannot semctl (sid)", TRUE),
X      exit (14);
X    free ((struct semid_ds *) sembuf.buf);
X
X    if (getuid () & geteuid ())
X      report_progress (report, "WARNING: serverd not started with root \
Xprivileges", TRUE);
X    if ((chpid = fork()) < 0)
X      report_progress (report, "\nmain(): Master process cannot fork Listener \
Xprocess", TRUE),
X      exit (12);
X    else if (chpid == 0) { /* Child process */
X      chpid = getpid ();   /* Set for signal handling purposes */
X      signal (SIGINT, (void (*)()) sighandle);
X# ifdef SIGCLD
X      signal (SIGCLD, (void (*)()) sighandle);
X# else
X      signal (SIGCHLD, (void (*)()) sighandle);
X# endif
X      signal (SIGTERM, (void (*)()) sighandle);
X      signal (SIGSEGV, (void (*)()) sighandle);
X      signal (SIGILL, (void (*)()) sighandle);
X      signal (SIGQUIT, (void (*)()) sighandle);
X      signal (SIGBUS, (void (*)()) sighandle);
X      strcpy (version, VERSION);
X      ch = strchr (version, ' ');
X      *ch = EOS;
X
X      if ((sock_fd = create_connection ()) < 0)
X	report_progress (report, "\nmain(): Listener process cannot create \
Xconnection", TRUE),
X	kill (ppid, ABORT_SIG),
X	exit (13);
X      if (setuid (pwentry->pw_uid) || setgid (pwentry->pw_gid))
X	report_progress (report, "\nmain(): Listener process cannot \
Xsetuid()/setgid()", TRUE),
X	kill (ppid, ABORT_SIG),
X	exit (15);
X      while (007) { /* Listen for ever */
X
X	l = sizeof (client);
X	if ((msg_sock = accept (sock_fd, (struct sockaddr *) &client,
X				(int *) &l)) < 0) {
X	  if (errno != EINTR
X#ifdef ERESTART
X	      && errno != ERESTART
X#endif
X	      )
X	    report_progress (report, "\nmain(): Listener process cannot accept()",
X			     TRUE),
X	    kill (ppid, ABORT_SIG),
X	    exit (13);
X	  continue;
X	}
X	else { /* New client; see if we can handle it */
X	  hp = gethostbyaddr ((char *) &client.sin_addr,
X			      sizeof (struct in_addr), client.sin_family);
X	  report_progress (report,
X			   tsprintf ("Connection request from %s: ",
X				     upcase ((s = (hp ? hp->h_name :
X						   (char *) inet_ntoa (client.sin_addr))))),
X			   FALSE);
X	  if (!stat (PRIVILEGED_HOSTSF, &stat_buf))
X	    if (!host_listed (PRIVILEGED_HOSTSF, s, report)) {
X	      CANNOT_CONNECT ("not a privileged host", CONN_ABORTED,
X			      "Not a privileged host.\n");
X	      continue;
X	    }
X	  if (host_listed (UNWANTED_HOSTSF, s, report)) {
X	    CANNOT_CONNECT ("unwanted host", CONN_ABORTED,
X			    "Not a privileged host.\n");
X	    continue;
X	  }
X
X	  if (nforks < 0 || nforks > MAX_CONNECTIONS)
X	    report_progress (report, tsprintf ("\nmain(): Listener process: \
Xinternal error: nforks=%d", nforks), TRUE),
X	    kill (ppid, ABORT_SIG),
X	    exit (16);
X	  if (nforks + 1 > MAX_CONNECTIONS) { /* Too many connections */
X	    if ((pid = check_timeout (clients)) > 0) {
X	      IN_CRITICAL_SECTION ("main", 0);
X	      kill (pid, TIMEOUT_SIG);	/* Tell it to commit suicide */
X	      while (child_alive (pid, clients)); /* Wait */
X	      OUT_OF_CRITICAL_SECTION ("main");
X	    }
X	    else { /* No children have timed out, so tough */
X	      CANNOT_CONNECT ("refused", SERVER_BUSY, SERVER_BUSY_);
X	      continue;
X	    }
X	  }
X	  report_progress (report, tsprintf ("connected (client #%d)", connid),
X			   TRUE);
X	  /* Parent increments the number of times it has forked */
X	  _nforks = nforks;
X	  clients [nforks].time = time (0); /* Save time child spawned */
X	  clients [nforks].connid = connid++;
X	  strcpy (clients [nforks++].name,
X		  (hp ? hp->h_name : (char *) inet_ntoa (client.sin_addr)));
X# if defined (bsd)
X#  ifdef SIGCLD
X	  smask = sigblock (sigmask (SIGCLD));
X#  else
X	  smask = sigblock (sigmask (SIGCHLD));
X#  endif
X# elif defined (svr4) || defined (svr3)
X#  ifdef SIGCLD
X	  sighold (SIGCLD);
X#  else
X	  sighold (SIGCHLD);
X#  endif
X# endif
X	  if ((pid = fork ()) < 0) {	/* fork */
X	    CANNOT_CONNECT ("cannot fork", SYS_ERROR, "Remote system error.\n");
X# if defined (bsd)
X	    sigsetmask (smask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	    sigrelse (SIGCLD);
X#  else
X	    sigrelse (SIGCHLD);
X#  endif
X# endif
X	    continue;
X	  }
X	  else if (pid == 0) {		/* Child process */
X	    close (sock_fd);	/* Close unused parent socket */
X# ifdef SIGCLD
X	    signal (SIGCLD, SIG_DFL);
X# else
X	    signal (SIGCHLD, SIG_DFL);
X# endif
X	    signal (TIMEOUT_SIG, (void (*)()) close_connection);
X	    signal (ABORT_SIG, (void (*)()) close_connection);
X	    signal (SIGALRM, (void (*)()) close_connection);
X	    signal (SIGPIPE, (void (*)()) close_connection);
X	    signal (SIGINT, (void (*)()) SIG_IGN);
X	    signal (SIGTERM, (void (*)()) close_connection);
X	    signal (SIGSEGV, (void (*)()) close_connection);
X	    signal (SIGILL, (void (*)()) close_connection);
X	    signal (SIGQUIT, (void (*)()) close_connection);
X	    signal (SIGBUS, (void (*)()) close_connection);
X	    alarm (conn_duration);
X	    time_is = time (0);
X	    sprintf (welcome, "%s interactive UNIX ListServer.\nVersion %s %s\
XMaximum connection duration is %d seconds.\n",
X		     sys.organization, version, ctime (&time_is),
X		     conn_duration);
X	    if ((f = fopen (WELCOMEF, "r"))) {
X	      while (!feof (f))
X		fgets (welcome + strlen (welcome), MAX_LINE, f);
X	      fclose (f);
X	    }
X	    sprintf (msg, "%d %d %s %d %d\n%d %d \n\n%s", CONNECT,
X				       conn_duration, version, strlen (PROMPT),
X				       strlen (CONT_PROMPT), OK,
X				       strlen (welcome) +
X				       strlen (LOGIN) + 1, welcome);
X	    if (write_to_fd (msg_sock, msg, strlen (msg)) < 0)
X	      CLIENT_LOST (13);
X	    process_live_request (msg_sock, client);
X	  }
X	  else
X	    clients [_nforks].pid = pid,
X# if defined (bsd)
X	    sigsetmask (smask),
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	    sigrelse (SIGCLD),
X#  else
X	    sigrelse (SIGCHLD),
X#  endif
X# endif
X	    close (msg_sock);
X	}
X      }
X    }
X  }
X#endif
X
X#ifdef GO_INTERACTIVE
X  if (setuid (pwentry->pw_uid) || setgid (pwentry->pw_gid))
X    report_progress (report, "\nmain(): Master process cannot \
Xsetuid()/setgid()", TRUE),
X    gexit (15);
X#endif
X
X  serverd_config ();
X  while (007) {
X    for (i = 0; i < nlists; ++i) {
X
X      if (!stat (lists[i].list_mail_f, &stat_buf) && stat_buf.st_size > 0 &&
X	  (rlfd = lock_file (lists[i].list_mail_f, O_RDWR, 0, FALSE)) >= 0) {
X	unlock_file (rlfd);
X
X	if (sys.lists[i].max_messages) {
X	  time (&time_is);
X	  t = localtime (&time_is);
X	  new_date = t->tm_mon + 1 + t->tm_mday + t->tm_year;
X	  if (new_date != lists[i].current_date)
X	    lists[i].nmessages = 0,
X	    lists[i].done_for_the_day = FALSE,
X	    lists[i].current_date = new_date,
X	    echo (tsprintf ("0 %d %d %d", t->tm_mon + 1, t->tm_mday,
X			    t->tm_year),
X		  lists[i].list_limitsf);
X	  if (!lists[i].done_for_the_day) {
X	    if ((f = fopen (lists[i].list_limitsf, "r"))) {
X	      fscanf (f, "%d", &lists[i].nmessages);
X	      fclose (f);
X	    }
X	    if (lists[i].nmessages >= sys.lists[i].max_messages) {
X	      lists[i].done_for_the_day = TRUE;
X	      lists[i].nmessages = 0;
X	      goto xxx;
X	    }
X	  }
X	  else
X	    goto xxx;
X	}
X
X	report_progress (report, tsprintf ("\n--- NEW MAIL FOR %s ---\n",
X					   sys.lists[i].alias), FALSE);
X
X	if (loadr) /* enforce restrictions */
X	  load_wait (max_load);
X
X	RESET (list);
X	sprintf (list, "%s %s %s", LIST, sys.lists[i].alias,
X		 sys.lists[i].cmdoptions);
X#ifdef GO_INTERACTIVE
X	IN_CRITICAL_SECTION ("main", 0);
X#endif
X	run_program (list, FALSE, -1);
X#ifdef GO_INTERACTIVE
X	OUT_OF_CRITICAL_SECTION ("main");
X#endif
X      xxx: ;
X      }
X/*
X      if (sys.frequency > 0)
X        sleep (sys.frequency);     do a quickie to The Spy Who Loved Him
X*/
X      if (!stat (lists[i].digest_msgf, &stat_buf) && stat_buf.st_size > 0) {
X        /*
X	   Something in the digest message file, see how long it's been
X	   since last message was sent by reading digest_timef.
X        */
X        if (fp = fopen (lists[i].digest_timef, "r"))
X	  fscanf (fp, "%ld\n", &time_then),
X	  fclose (fp);
X        if ((time(0) - time_then) / 3600 >= sys.lists[i].digest_hours) {
X	  report_progress (report,
X			   tsprintf ("\n--- DIGEST TIME REACHED FOR %s ---\n",
X				     sys.lists[i].alias), FALSE);
X
X          if (loadr)
X	    load_wait (max_load);
X
X          RESET (list);
X          sprintf (list, "%s %s %s -d", LIST, sys.lists[i].alias,
X	           sys.lists[i].cmdoptions);
X#ifdef GO_INTERACTIVE
X	  IN_CRITICAL_SECTION ("main", 0);
X#endif
X          run_program (list, FALSE, -1);
X#ifdef GO_INTERACTIVE
X	  OUT_OF_CRITICAL_SECTION ("main");
X#endif
X        }
X      }
X    }
X
X    if (!stat (BATCH_FILE, &stat_buf) && stat_buf.st_size > 0) {
X      time (&time_is);
X      t = localtime (&time_is);
X      new_date = t->tm_mon + 1 + t->tm_mday + t->tm_year;
X      if (new_date != current_date) { /* Process batch on new day only */
X	current_date = new_date; /* New day or first time through loop */
X	report_progress (report, "\n--- PROCESSING THE BATCH QUEUE ---\n",
X			 FALSE);
X	RESET (server);
X	sprintf (server, "%s %s -B", SERVER, sys.server.cmdoptions);
X#ifdef GO_INTERACTIVE
X	IN_CRITICAL_SECTION ("main", 0);
X#endif
X	run_program (server, TRUE, -1);
X#ifdef GO_INTERACTIVE
X	OUT_OF_CRITICAL_SECTION ("main");
X#endif
X      }
X    }
X
X    if (!stat (SERVER_MAIL_FILE, &stat_buf) && stat_buf.st_size > 0 &&
X	(rlfd = lock_file (SERVER_MAIL_FILE, O_RDWR, 0, FALSE)) >= 0) {
X      unlock_file (rlfd);
X
X      time (&time_is);
X      t = localtime (&time_is);
X      new_date = t->tm_mon + 1 + t->tm_mday + t->tm_year;
X      current_date = new_date; /* New day or first time through loop */
X      report_progress (report, "\n--- NEW MAIL FOR SERVER ---\n", FALSE);
X
X      if (loadr) /* Enforce restrictions */
X	load_wait (max_load);
X
X      RESET (server);
X      sprintf (server, "%s %s", SERVER, sys.server.cmdoptions);
X#ifdef GO_INTERACTIVE
X      IN_CRITICAL_SECTION ("main", 0);
X#endif
X      run_program (server, TRUE, -1);
X#ifdef GO_INTERACTIVE
X      OUT_OF_CRITICAL_SECTION ("main");
X#endif
X    }
X
X    if (execute_once)
X      gexit (0);
X    if (sys.frequency > 0)
X      sleep (sys.frequency);     /* do a quickie to Money Penny */
X  }
X}
X
Xvoid serverd_config ()
X{
X  int i, mon, day, year;
X  FILE *f;
X
X  for (i = 0; i < nlists; ++i) {
X    setup_string (lists[i].list_mail_f, sys.lists[i].alias, LIST_MAIL_FILE);
X    setup_string (lists[i].digest_msgf, sys.lists[i].alias, DIGEST_MSG);
X    setup_string (lists[i].digest_timef, sys.lists[i].alias, DIGEST_TIME);
X    setup_string (lists[i].list_limitsf, sys.lists[i].alias, LIST_LIMITS);
X    lists[i].nmessages = mon = day = year = 0;
X    lists[i].done_for_the_day = FALSE;
X    if ((f = fopen (lists[i].list_limitsf, "r")))
X      fscanf (f, "%d %d %d %d", &lists[i].nmessages, &mon, &day, &year),
X      fclose (f);
X    lists[i].current_date = mon + day + year;
X  }
X}
X
Xvoid usage ()
X{
X#ifdef GO_INTERACTIVE
X  fprintf (stderr, "Usage: serverd [-1] [-e] [-l load] [-i duration]\n\
X-1: Execute only once.\n\
X-e: Echo reports to the screen.\n\
X-l: Enforce load restrictions.\n\
X-i: Go into interactive mode also; listen for TCP connections;\n\
X    each connection is limited to 'duration' seconds.\n");
X#else
Xfprintf (stderr, "Usage: serverd [-1] [-e] [-l load]\n\
X-1: Execute only once.\n\
X-e: Echo reports to the screen.\n\
X-l: Enforce load restrictions.\n");
X#endif
X  exit (3);
X}
X
X/*
X  Graceful exit. Remove pid file.
X*/
X
Xint gexit (int exitcode)
X{
X  unlink (PID_SERVERD);
X#ifndef NO_LOCKS
X  unlock_file (lfd);
X#endif
X#ifdef GO_INTERACTIVE
X  if (interactive) {
X    semremove (sid);
X    kill (chpid, SIGINT);
X    while (!chdied);
X  }
X#endif
X  exit (exitcode);
X}
X
X/*
X  Wait MAX_TRIES * 30 seconds for load to drop to max_load.
X
X  USER CONTRIBUTED MODIFIED CODE (for version 5.5): Warren Burstein.
X*/
X
Xvoid load_wait (float max_load)
X{
X  int try;
X  float load;
X  FILE *loadf;
X
X  for (try = 0 ;; try++) {
X#if defined (__NeXT__) || defined (unknown_port)
X    syscom ("%s | %s -F, '{ n=split($4,f,\" \");print f[3] }' > %s",
X	    UPTIME, AWK, LOAD_FILE);
X#elif defined (_AIX)
X    syscom ("%s | %s -F, '{ n=split($3,f,\" \");print f[2] }' > %s",
X	    UPTIME, AWK, LOAD_FILE);
X#else
X    syscom ("%s | %s -F, '{ print $4 }' | %s -d' ' -f5 > %s",
X            UPTIME, AWK, CUT, LOAD_FILE);
X#endif
X    OPEN_FILE (loadf, LOAD_FILE, "r", "main");
X    fscanf (loadf, "%f", &load);
X    fclose (loadf);
X    unlink (LOAD_FILE);
X
X    if (load <= max_load)
X      break;
X
X    sleep (30);
X  }
X}
X
X/*
X  Execute 'command' using the system call.
X
X  USER CONTRIBUTED MODIFIED CODE (for version 5.5): Warren Burstein.
X*/
X
Xvoid run_program (char *command, BOOLEAN is_listserv, int msg_sock)
X{
X  int status, mask;
X  char msg [MAX_LINE];
X  char reply [MAX_LINE];
X
X  report_progress (report, command, TRUE);
X
X#ifdef GO_INTERACTIVE
X  if (ppid == getpid())
X# if defined (bsd)
X#  ifdef SIGCLD
X    mask = sigblock (sigmask (SIGCLD)),
X#  else
X    mask = sigblock (sigmask (SIGCHLD)),
X#  endif
X    status = system (command),
X    sigsetmask (mask);
X# elif (defined (svr4) || defined (svr3)) && !defined (stellar)
X#  ifdef SIGCLD
X    sighold (SIGCLD),
X#  else
X    sighold (SIGCHLD),
X#  endif
X    status = system (command),
X#  ifdef SIGCLD
X    sigrelse (SIGCLD);
X#  else
X    sigrelse (SIGCHLD);
X#  endif
X# else
X#  ifdef SIGCLD
X    signal (SIGCLD, SIG_DFL),
X#  else
X    signal (SIGCHLD, SIG_DFL),
X#  endif
X    status = system (command),
X#  ifdef SIGCLD
X    signal (SIGCLD, (void (*)()) sighandle);
X#  else
X    signal (SIGCHLD, (void (*)()) sighandle);
X#  endif
X# endif
X  else
X#endif
X    status = system (command);
X  if (status > 127) {
X    sprintf (msg, "%s %s: %s: %s",
X	     (ppid == getpid() ? "serverd" : "IUL client"),
X	     (WEXITSTATUS (status) == 7) ? "restarts" : "died",
X             is_listserv ? "LISTSERV" : "LIST",
X             (WEXITSTATUS (status) >= 0 && WEXITSTATUS (status) <= 16 ?
X	      exit_string[WEXITSTATUS (status)] : exit_string[17]));
X    if (sys.options & BSD_MAIL)
X      syscom ("echo '' | %s -s '%s' %s", UCB_MAIL, msg, sys.manager);
X    report_progress (report, msg, TRUE);
X
X    if (is_listserv) {
X      if (WEXITSTATUS (status) == 6) { /* Shutdown request */
X#ifdef SYSLOG
X	closelog ();
X#else
X	fclose (report);
X#endif
X#ifdef GO_INTERACTIVE
X	if (interactive) {
X	  CLOSE_CLIENT (msg_sock);
X	  if (ppid == getpid()) /* Master process notifies */
X	    gexit (6);	/* It will kill chpid */
X	  exit (6);
X	}
X#endif
X	gexit (6);
X      }
X
X      if (WEXITSTATUS (status) == 7) { /* Restart request */
X	unlink (PID_SERVERD);
X#ifndef NO_LOCKS
X        unlock_file (lfd);
X#endif
X#ifdef GO_INTERACTIVE
X	if (interactive) {
X	  CLOSE_CLIENT (msg_sock);
X	  if (ppid == getpid()) { /* Master process notifies and continues */
X	    semremove (sid);
X# ifdef SIGCLD
X	    signal (SIGCLD, SIG_DFL);
X# else
X	    signal (SIGCHLD, SIG_DFL);
X# endif
X	    kill (chpid, SIGINT);
X	    while (!chdied);
X	  }
X	  else
X	    exit (7);
X	}
X#endif
X	signal (SIGALRM, SIG_IGN); /* Just in case */
X        execl (START, START, START_OPTIONS, NULL);
X        sprintf (msg, "serverd commits suicide: Could not restart \
Xsystem");
X        report_progress (report, msg, TRUE);
X#ifdef SYSLOG
X        closelog ();
X#else
X	fclose (report);
X#endif
X        if (sys.options & BSD_MAIL)
X          syscom ("echo '' | %s -s '%s' %s", UCB_MAIL, msg, sys.manager);
X#ifdef GO_INTERACTIVE
X	if (interactive)
X	  exit (5);
X#endif
X	gexit (5);
X      }
X    }
X#ifdef SYSLOG
X    closelog ();
X#else
X    fclose (report);
X#endif
X#ifdef GO_INTERACTIVE
X    if (interactive) {
X      CLOSE_CLIENT (msg_sock);
X      if (ppid == getpid()) /* Master process notifies */
X	gexit (WEXITSTATUS (status));	/* Will kill chpid */
X      exit (WEXITSTATUS (status));
X    }
X#endif
X    gexit (WEXITSTATUS (status));
X  }
X
X  if (status == 127) {
X    sprintf (msg, "serverd died: could not execute shell for %s",
X             is_listserv ? "listserv" : "list");
X    if (sys.options & BSD_MAIL)
X      syscom ("echo '' | %s -s '%s' %s", UCB_MAIL, msg, sys.manager);
X    report_progress (report, msg, TRUE);
X#ifdef SYSLOG
X    closelog ();
X#else
X    fclose (report);
X#endif
X#ifdef GO_INTERACTIVE
X    if (interactive)
X      exit (5);
X#endif
X    gexit (5);
X  }
X  else if (status > 0 && status < 127) {
X    sprintf (msg, "serverd commits suicide: status %d while trying to \
Xspawn %s: no tty; see the documentation", status, command);
X    report_progress (report, msg, TRUE);
X#ifdef SYSLOG
X    closelog ();
X#else
X    fclose (report);
X#endif
X#ifdef GO_INTERACTIVE
X    if (interactive)
X      exit (5);
X#endif
X    gexit (5);
X  }
X  else if (status < 0)
X    report_progress (report, "Cannot get child exit status", TRUE);
X}
X
X#ifdef GO_INTERACTIVE
X/*
X  Handle signals, especially SIGCLD.
X*/
X
Xvoid sighandle (int sig)
X{
X#ifdef WAIT3_NEEDS_UNION
X  union wait status;
X#else
X  int status = 1;
X#endif
X  int pid, i, j, _nforks, mask, pids[MAX_CONNECTIONS], exstatus;
X  char reply [MAX_LINE];
X  BOOLEAN restart = FALSE;
X
X# ifdef SIGCLD
X  if (sig != SIGCLD)
X    signal (sig, SIG_IGN);
X  if (sig == SIGCLD) {
X# else
X  if (sig != SIGCHLD)
X    signal (sig, SIG_IGN);
X  if (sig == SIGCHLD) {
X# endif
X# if defined (bsd)
X#  ifdef SIGCLD
X    mask = sigblock (sigmask (SIGCLD));
X#  else
X    mask = sigblock (sigmask (SIGCHLD));
X#  endif
X# elif defined (svr4) || defined (svr3)
X#  ifdef SIGCLD
X    sighold (SIGCLD);
X#  else
X    sighold (SIGCHLD);
X#  endif
X# endif
X    if (ppid == getpid()) {	/* Listener process died, or system() bug */
X# if defined (sequent) || defined (stardent) || defined (stellar) || \
X  defined (titan) || defined (unknown_port)
X      do {
X	pid = wait3 (&status, WNOHANG, NULL);
X      } while (pid && pid != chpid);
X# else
X      pid = waitpid (chpid, &status, WNOHANG);
X# endif
X      if (!pid) {	/* system() bug */
X# if defined (bsd)
X	sigsetmask (mask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	sigrelse (SIGCLD);
X#  else
X	sigrelse (SIGCHLD);
X#  endif
X# endif
X	signal (sig, (void (*)()) sighandle);
X	return;
X      }
X    }
X    else	/* Listener process: "live" child died */
X      pid = wait (&status);   /* Get child's pid */
X    for (i = 0; i < nforks; i++) {
X      if (clients [i].pid == pid) { /* Remove dead child's pid */
X	report_progress (report,
X			 tsprintf ("Connection closed with %s (client #%d)",
X				   clients [i].name, clients [i].connid), TRUE);
X	for (j = i; j < nforks - 1; j++)
X	memcpy ((char *) &clients [j], (char *) &clients [j + 1], 
X		sizeof (CLIENT));
X	break;
X      }
X    }
X    if (i == nforks && chpid == getpid())
X      report_progress (report, "Connection closed with last client", TRUE);
X    --nforks;
X    if (!semctl (sid, 0, GETVAL) && semctl (sid, 0, GETPID) == pid)
X      if (V (sid) < 0) /* Out of critical section */
X	  report_progress (report, tsprintf ("\nsighandle(): %s process: \
XError V(). Unexpected behavior is imminent",
X					     (chpid == getpid () ? "Listener" :
X					      "Master")), TRUE);
X    if (WIFSIGNALED (status))  {
X      if (WTERMSIG (status) > SIGINT &&
X	  WTERMSIG (status) != ABORT_SIG && WTERMSIG (status) != TIMEOUT_SIG)
X	report_progress (report, tsprintf ("\nsighandle(): Child died \
Xabnormally: signal %d",
X					   WTERMSIG (status)), TRUE);
X    }
X    else if (WIFEXITED (status)) { /* Abnormal child exit status ? */
X      if ((exstatus = WEXITSTATUS (status)) == 0 && pid != chpid) {
X# if defined (bsd)
X	sigsetmask (mask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	sigrelse (SIGCLD);
X#  else
X	sigrelse (SIGCHLD);
X#  endif
X# endif
X	signal (sig, (void (*)()) sighandle);
X	return;
X      }
X      if (exstatus == 6 || exstatus == 7 || exstatus == 8 || exstatus == 11 ||
X	  exstatus == 13 || exstatus == 14 || exstatus == 15 || exstatus == 16) {
X       /* Shutdown or restart request, semaphore/socket error */
X	if (exstatus == 7 && ppid == getpid())
X	  restart = TRUE; /* Only master process may restart */
X	else if (exstatus == 8)
X	  report_progress (report, "\nsighandle(): Child received SIGTERM",
X			   TRUE);
X	else if (exstatus == 11 || exstatus == 13 || exstatus == 15 ||
X		 exstatus == 16) {
X	  report_progress (report,
X			   tsprintf ("\nsighandle(): Child experienced %s \
Xerror", (exstatus == 11 ? "malloc()" : (exstatus == 13 ? "socket" :
X(exstatus == 15 ? "setuid()/setgid()" : "internal")))), TRUE);
X# if defined (bsd)
X	  sigsetmask (mask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	  sigrelse (SIGCLD);
X#  else
X	  sigrelse (SIGCHLD);
X#  endif
X# endif
X	  signal (sig, (void (*)()) sighandle);
X	  return;
X	}
X	else if (exstatus == 14)
X	  report_progress (report, "\nsighandle(): Child experienced semaphore\
X error", TRUE);
X	if (ppid != getpid()) { /* Master process will exit gracefully */
X	  _nforks = nforks; /* As children die nforks decreases */
X# if defined (bsd)
X	  sigsetmask (mask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X	  sigrelse (SIGCLD);
X#  else
X	  sigrelse (SIGCHLD);
X#  endif
X# endif
X	  signal (sig, (void (*)()) sighandle);
X	  for (i = 0; i < _nforks; i++)
X	    pids[i] = clients [i].pid;
X	  for (i = 0; i < _nforks; i++) /* Kill any "live" children */
X	    kill (pids[i], ABORT_SIG);
X	  kill (ppid, ABORT_SIG);
X	  exit (exstatus);
X	}
X      }
X    }
X# if defined (bsd)
X    sigsetmask (mask);
X# elif defined (svr3) || defined (svr4)
X#  ifdef SIGCLD
X    sigrelse (SIGCLD);
X#  else
X    sigrelse (SIGCHLD);
X#  endif
X# endif
X    signal (sig, (void (*)()) sighandle);
X    if (ppid != getpid()) /* Master process will die */
X      return;
X    else if (pid != chpid) /* Master cares only about the Listener process */
X      return;
X    unlink (PID_SERVERD);
X  }
X# ifdef SIGCLD
X  signal (SIGCLD, SIG_DFL);
X# else
X  signal (SIGCHLD, SIG_DFL);
X# endif
X  if (chpid == getpid()) { /* Listener process */
X    for (i = 0; i < nforks; i++) { /* Kill any "live" children */
X      report_progress (report, tsprintf ("Connection closed with %s (client #%d)",
X					 clients [i].name, clients [i].connid),
X		       TRUE);
X      kill (clients [i].pid, ABORT_SIG);
X    }
X    kill (ppid, ABORT_SIG);  /* Notify master process we are dying */
X  }
X  /* Next two lines are currently useless; kept for future use */
X  if (getpid() != chpid && getpid() != ppid) /* Live process */
X    CLOSE_CLIENT (msg_sock);
X  if (ppid == getpid ())
X    semremove (sid);	/* Only master process may remove it */
X  if (restart) {
X# ifndef NO_LOCKS
X    unlock_file (lfd);
X# endif
X    execl (START, START, START_OPTIONS, NULL);
X    report_progress (report, "serverd commits suicide: Could not restart system",
X		     TRUE);
X# ifdef SYSLOG
X    closelog ();
X# else
X    fclose (report);
X# endif
X    if (sys.options & BSD_MAIL)
X      syscom ("echo '' | %s -s '%s' %s", UCB_MAIL, reply, sys.manager);
X    exit (5);
X  }
X# ifndef NO_LOCKS
X  unlock_file (lfd);
X# endif
X# ifdef SYSLOG
X  closelog ();
X# else
X  fclose (report);
X# endif
X  exit (0);
X}
X
X/*
X  Called when Listener process has exited.
X*/
X
Xvoid listener_exited ()
X{
X  chdied = TRUE;
X}
X
X/*
X  Child has been notified that it should commit suicide, so it does after
X  notifying the client.
X*/
X
Xvoid close_connection (int sig)
X{
X  char s [256];
X
X  signal (sig, SIG_IGN);
X  if (sig == SIGALRM && !semctl (sid, 0, GETVAL) &&
X      semctl (sid, 0, GETPID) == getpid()) {
X    signal (SIGALRM, (void (*)()) close_connection);
X    alarm (5);
X    return;
X  }
X  if (sig == TIMEOUT_SIG || sig == SIGALRM)
X    sprintf (s, "%d %d \n%s", CONN_TIMEOUT, strlen (CONN_TIMED_OUT),
X	     CONN_TIMED_OUT);
X  else if (sig == SIGTERM)
X    sprintf (s, "%d %d \n%s", CONN_CLOSED, strlen (GOOD_BYE), GOOD_BYE);
X  else if (sig == ABORT_SIG)
X    sprintf (s, "%d %d \n%s", CONN_ABORTED, strlen (SERVER_SHUTS_DOWN),
X	     SERVER_SHUTS_DOWN);
X  else
X    sprintf (s, "%d %d \n%s", CONN_ABORTED, strlen (REMOTE_SYS_ERROR),
X	     REMOTE_SYS_ERROR);
X  write_to_fd (msg_sock, s, strlen (s));
X  close (msg_sock);
X#ifndef SYSLOG
X  fclose (report);
X#endif
X  unlink (requests_file);
X  _exit (0);
X}
X
X/*
X  Check whether a child process has been running longer than 'conn_duration'
X  seconds and return its pid, or 0 otherwise.
X*/
X
Xint check_timeout (CLIENT clients [])
X{
X  int i;
X
X  for (i = 0; i < nforks; i++)
X    if ((time (0) - clients [i].time) > conn_duration)
X      return clients [i].pid;
X  return 0;
X}
X
X/*
X  Create a socket and bind it to the local address and to an available port.
X  Return the socket file descriptor, or -1 on error.
X*/
X
Xint create_connection ()
X{
X  struct sockaddr_in server;
X  struct servent *service;
X  struct hostent *hostentry;
X  int sock, timeout = 0, sendbuf = BUFFSIZ, recvbuf = BUFFSIZ, val = 1;
X
X#ifndef IUL_PORT
X  if (! (service = getservbyname (SERVICE, NULL))) {
X    report_progress (report, "\ncreate_connection(): Service unavailable",
X		     TRUE);
X    return -1;
X  }
X#endif
X  if (! (hostentry = gethostbyname ("localhost"))) {
X    report_progress (report, "\ncreate_connection(): No such host", TRUE);
X    return -1;
X  }
X  if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X    report_progress (report, "\ncreate_connection(): Could not create socket",
X		     TRUE);
X    return -1;
X  }
X  if (setsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		  sizeof (sendbuf)) < 0)
X    report_progress (report, "\ncreate_connection(): WARNING: Could not \
Xset send-buffer size: setsockopt() error", TRUE);
X  if (setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		  sizeof (recvbuf)) < 0)
X    report_progress (report, "\ncreate_connection(): WARNING: Could not \
Xset receive-buffer size: setsockopt() error", TRUE);
X  if (setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &val,
X      sizeof (val)) < 0)
X    report_progress (report, "\ncreate_connection():WARNING: Cannot toggle \
Xkeep-alive connections: setsockopt() error", TRUE);
X  val = 1;
X  if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
X      sizeof (val)) < 0)
X    report_progress (report, "\ncreate_connection():WARNING: Cannot toggle \
Xreuse of local address: setsockopt() error", TRUE);
X
X  server.sin_family = AF_INET;            /* Internet protocol */
X  server.sin_addr.s_addr = INADDR_ANY;
X#ifndef IUL_PORT
X  server.sin_port = service->s_port;
X#else
X  server.sin_port = htons (IUL_PORT);
X#endif
X
X  while (bind (sock, (struct sockaddr *) &server, sizeof (server)) < 0 &&
X	 errno == EADDRINUSE && timeout < 180) { /* For perm port keep trying */
X    ++timeout;
X    errno = 0;
X    sleep (1);
X  }
X  if (timeout >= 180) {
X    report_progress (report, "\ncreate_connection(): Could not bind", TRUE);
X    return -1;
X  }
X  listen (sock, 5);
X  return sock;
X}
X
X/*
X  Process all live requests coming in from the tcp connection.
X
X  PLEASE DO NOT MODIFY THIS ROUTINE AS YOU MAY BREAK THE PROTOCOL AND CREATE
X  HAVOC WITH PEER INTERACTIVE UNIX LISTSERVERS. A LOT OF UNDOCUMENTED
X  ASSUMPTIONS ARE MADE. YOU SHOULD CONTACT ME ABOUT ANY CHANGES YOU WISH TO
X  MAKE.
X
X  [What a messy routine]
X*/
X
Xvoid process_live_request (int msg_sock, struct sockaddr_in client)
X{
X  char ch, *buf, *dash, server [MAX_LINE], msg [MAX_LINE];
X  char denied_req [6][MAX_LINE], login [MAX_LINE], *passwd;
X  char *perms [6], *s, *r, *rr;
X  char outfile [MAX_LINE], reply [MAX_LINE], arg [MAX_LINE];
X  char localhost [MAX_LINE], _localhost [MAX_LINE], localaddr [MAX_LINE];
X  char rhost [MAX_LINE], raddr [MAX_LINE];
X  FILE *f;
X  long int t, time_started, sig_mask, naddr, naddr2, nlines, buffsiz = BUFFSIZ;
X  long int i, j;
X  int response, fmode, l, auth = NOTSUBSCRIBED, bytes_read = 0, rcode, err;
X  int listid;
X  BOOLEAN more_input = FALSE, reply_code, bin = TRUE, put_request,
X    already_continued = FALSE;
X  struct hostent *hp, *rhp;
X  struct stat stat_buf;
X  struct in_addr _addr;
X
X  sprintf (denied_req [MANAGER], "-d execute -d fax");
X  sprintf (denied_req [OWNER], "%s -d shutdown -d restart",
X	   denied_req [MANAGER]);
X  sprintf (denied_req [SUBSCRIBED], "%s -d subscribe -d system -d reports \
X-d edit -d put -d approve -d discard", denied_req [OWNER]);
X  sprintf (denied_req [NOTSUBSCRIBED], "%s -d unsubscribe -d set -d which \
X-d run",
X	   denied_req [SUBSCRIBED]);
X  perms [MANAGER] = MANAGER_LOGIN;
X  perms [OWNER] = OWNER_LOGIN;
X  perms [SUBSCRIBED] = SUBSCRIBER_LOGIN;
X  perms [NOTSUBSCRIBED] = GENERAL_LOGIN;
X
X  time_started = time (0);
X  gethostname (localhost, sizeof (localhost));
X  strcpy (_localhost, localhost);
X  upcase (_localhost);
X  if (write_to_fd (msg_sock, LOGIN, strlen (LOGIN)) < 0)
X    CLIENT_LOST (13);
X  outfile[0] = RESET (login);
X  GET_LOGIN (login);
X  if (!strcmp (login, "__abort__")) /* Client aborted -- Reserved */
X    goto abort;
X  if (login [0] != EOS) { /* Proceed with authentication */
X    strcpy (msg, login);  /* Used to form a "From " line */
X    rr = s = mystrdup (login);
X    upcase (s);
X    while ((s = _strstr (s, "IUL:"))) {
X      s += 4;	/* Point to remote host */
X      sscanf (s, "%s", rhost);
X      if ((r = strchr (rhost, ';')))
X	*r = EOS;
X      if (!strcmp (rhost, _localhost)) { /* Loop */
X	sprintf (msg, "Loop detected.\n");
X	REPLY (msg_sock, PEER_UNAVAIL, strlen (msg));
X	write_to_fd (msg_sock, msg, strlen (msg));
X	goto ciao;
X      }
X      rhp = hp = gethostbyname (rhost);
X      if (hp) {
X	if ((rhp = (struct hostent *) malloc (sizeof (struct hostent))) == NULL)
X	  report_progress (report, "\nprocess_live_request(): malloc() failed",
X			   TRUE),
X	  exit (11);
X	memcpy ((char *) rhp, (char *) hp, sizeof (*hp));
X#ifdef h_addr
X	if ((rhp->h_addr_list = (char **) malloc (sizeof (char *))) == NULL)
X	  report_progress (report, "\nprocess_live_request(): malloc() failed",
X			   TRUE),
X	  exit (11);
X	naddr = 0;
X	while (hp->h_addr_list[naddr]) {
X	  if ((rhp->h_addr_list = (char **)
X	       realloc (rhp->h_addr_list, (naddr + 2) * sizeof (char *))) ==
X	      NULL)
X	    report_progress (report,
X			     "\nprocess_live_request(): realloc() failed", TRUE),
X	    exit (11);
X	  if ((rhp->h_addr_list[naddr] = (char *)
X	       malloc (rhp->h_length * sizeof (char))) == NULL)
X	    report_progress (report,
X			     "\nprocess_live_request(): malloc() failed", TRUE),
X	    exit (11);
X	  memcpy ((char *) rhp->h_addr_list[naddr],
X		  (char *) hp->h_addr_list[naddr], hp->h_length);
X	  rhp->h_addr_list[++naddr] = NULL;
X	}
X	naddr = 0;
X	while (rhp->h_addr_list[naddr]) {
X	  memcpy ((char *) &_addr, (char *) rhp->h_addr_list[naddr++],
X		  rhp->h_length);
X	  strcpy (raddr, (char *) inet_ntoa (_addr));
X	  hp = gethostbyname (localhost);
X	  naddr2 = 0;
X	  while (hp && hp->h_addr_list[naddr2]) {
X	    memcpy ((char *) &_addr, (char *) hp->h_addr_list[naddr2++],
X		    hp->h_length);
X	    strcpy (localaddr, (char *) inet_ntoa (_addr));
X	    if (!strcmp (raddr, localaddr)) { /* Loop */
X	      sprintf (msg, "Loop detected.\n");
X	      REPLY (msg_sock, PEER_UNAVAIL, strlen (msg));
X	      write_to_fd (msg_sock, msg, strlen (msg));
X	      goto ciao;
X	    }
X	  }
X	}
X	naddr = 0;
X	while (rhp->h_addr_list[naddr])
X	  free ((char *) rhp->h_addr_list[naddr++]);
X	free ((char **) rhp->h_addr_list);
X#else
X	if ((rhp->h_addr = (char *) malloc ((strlen (hp->h_addr) + 1) *
X					    sizeof (char))) == NULL) {
X	  report_progress (report, "\nsiul(): malloc() failed", TRUE);
X	  close (ofd);
X	  return -1;
X	}
X	memcpy ((char *) rhp->h_addr, (char *) hp->h_addr, hp->h_length);
X	memcpy ((char *) &_addr, (char *) rhp->h_addr, rhp->h_length);
X	strcpy (raddr, (char *) inet_ntoa (_addr));
X	hp = gethostbyname (rhost);
X	memcpy ((char *) &_addr, (char *) hp->h_addr, hp->h_length);
X	strcpy (localaddr, (char *) inet_ntoa (_addr));
X	if (!strcmp (raddr, localaddr)) { /* Loop */
X	  write_to_fd (ofd, "Loop detected.\n", 15);
X	  cmd = PEER_UNAVAIL;
X	  goto abort;
X	}
X	free ((char *) rhp->h_addr);
X#endif
X	free ((struct hostent *) rhp);
X      }
X      hp = gethostbyname (localhost);
X      naddr = 0;
X#ifdef h_addr
X      while (hp && hp->h_addr_list[naddr]) {
X	memcpy ((char *) &_addr, (char *) hp->h_addr_list[naddr++],
X		hp->h_length);
X	strcpy (localaddr, (char *) inet_ntoa (_addr));
X	if (!strcmp (rhost, localaddr)) { /* Loop */
X	  sprintf (msg, "Loop detected.\n");
X	  REPLY (msg_sock, PEER_UNAVAIL, strlen (msg));
X	  write_to_fd (msg_sock, msg, strlen (msg));
X	  goto ciao;
X	}
X      }
X#else
X      memcpy ((char *) &_addr, (char *) hp->h_addr, hp->h_length);
X      strcpy (localaddr, (char *) inet_ntoa (_addr));
X      if (!strcmp (rhost, localaddr)) { /* Loop */
X	sprintf (msg, "Loop detected.\n");
X	REPLY (msg_sock, PEER_UNAVAIL, strlen (msg));
X	write_to_fd (msg_sock, msg, strlen (msg));
X	goto ciao;
X      }
X#endif
X    }
X    free ((char *) rr);
X    REPLY (msg_sock, PASSWORD_REQUIRED, strlen (PASSWORD));
X    if (write_to_fd (msg_sock, PASSWORD, strlen (PASSWORD)) < 0)
X      CLIENT_LOST (13);
X    if ((passwd = (char *) malloc (MAX_LINE * sizeof (char))) == NULL)
X      report_progress (report,
X		       "\nprocess_live_request(): malloc() failed", TRUE),
X      exit (11);
X    RESET (passwd);
X    GET_LOGIN (passwd);
X    if (!strcmp (passwd, "__abort__")) /* Client aborted -- Reserved */
X      goto abort;
X    upcase (passwd);
X    if (!strcmp (login, sys.manager)) /* Check to see if he's the manager */
X      if (!strcmp (passwd, sys.server.password)) {
X	auth = MANAGER;
X	REPLY (msg_sock, OK, strlen (perms [auth]) + strlen (PROMPT));
X	if (write_to_fd (msg_sock, perms [auth], strlen (perms [auth])) < 0)
X	  CLIENT_LOST (13);
X	goto skip;
X      }
X    upcase (login);
X    for (listid = 0; listid < nlists; listid++) /* Check to see if he is an owner */
X      if (owner_listed (OWNERSF, login, sys.lists[listid].alias, report) ==
X	  OWNER)
X	if (!strcmp (passwd, sys.lists[listid].password)) {
X	  auth = OWNER;
X	  REPLY (msg_sock, OK, strlen (perms [auth]) + strlen (PROMPT));
X	  if (write_to_fd (msg_sock, perms [auth], strlen (perms [auth])) < 0)
X	    CLIENT_LOST (13);
X	  goto skip;
X	}
X
X    for (listid = 0; listid < nlists; listid++) { /* Check for regular subscription */
X      setup_string (subscribersf, sys.lists[listid].alias, SUBSCRIBERS);
X      sprintf (password_in_sub_file, "%d", time (0));
X      if (subscribed (report, login, subscribersf, NULL, NULL, NULL) ==
X	  SUBSCRIBED)
X	if (!strcmp (passwd, password_in_sub_file)) {
X	  auth = SUBSCRIBED;
X	  REPLY (msg_sock, OK, strlen (perms [auth]) + strlen (PROMPT));
X	  if (write_to_fd (msg_sock, perms [auth], strlen (perms [auth])) < 0)
X	    CLIENT_LOST (13);
X	  goto skip;
X        }
X    }
X  }
X  else
X    REPLY (msg_sock, OK, 0);
X  REPLY (msg_sock, OK, strlen (perms [auth]) + strlen (PROMPT));
X  if (write_to_fd (msg_sock, perms [auth], strlen (perms [auth])) < 0)
X    CLIENT_LOST (13);
X  hp = gethostbyaddr ((char *) &client.sin_addr,
X		      sizeof (struct in_addr), client.sin_family);
X  sprintf (msg, "IUL:%s;%s",
X	   (hp ? hp->h_name : (char *) inet_ntoa (client.sin_addr)),
X	   login);
X
X skip:
X  hp = gethostbyaddr ((char *) &client.sin_addr,
X		      sizeof (struct in_addr), client.sin_family);
X  if (write_to_fd (msg_sock, PROMPT, strlen (PROMPT)) < 0)
X    CLIENT_LOST (13);
X
X  if ((buf = (char *) malloc (buffsiz * sizeof (char))) == NULL)
X    report_progress (report,
X		     "\nprocess_live_request(): malloc() failed", TRUE),
X    exit (11);
X
X  ch = RESET (buf);
X
X  while (ch != EOF && bytes_read < MAX_LINE) { /* Get request & process it */
X    errno = 0;
X    if (read (msg_sock, &ch, 1) <= 0) {
X      l = sizeof (err);
X      if (!getsockopt (sock_fd, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l)) {
X	if (err)
X	  CLIENT_LOST (13);
X      }
X      else {
X	REPLY (msg_sock, SYS_ERROR, strlen (REMOTE_SYS_ERROR));
X	write_to_fd (msg_sock, REMOTE_SYS_ERROR, strlen (REMOTE_SYS_ERROR));
X	CLIENT_LOST (13);
X      }
X      if (errno == EINTR
X#ifdef ERESTART
X	  || errno == ERESTART
X#endif
X	  )
X	continue;
X      break;
X    }
X    if (time (0) - time_started > conn_duration) /* In case SIGALRM f_cks up */
X      close_connection (TIMEOUT_SIG);
X    ++bytes_read;
X    if (ch >= ' ' || (isspace (ch) && ch != '\r')) {
X      if (ch != '\n') /* Buffer request */
X	buf [(l = strlen (buf))] = ch,
X	buf [l + 1] = EOS;
X      else {	/* End of request? process it */
X	if (strlen (buf) && buf [strlen (buf) - 1] == '&' &&
X	    ! already_continued) { /* Request continues */
X	  buf [strlen (buf) - 1] = EOS; /* Replace & */
X	  REPLY (msg_sock, CONTINUED, strlen (CONT_PROMPT));
X	  if (write_to_fd (msg_sock, CONT_PROMPT, strlen (CONT_PROMPT)) < 0)
X	    CLIENT_LOST (13);
X	  already_continued = TRUE;
X	  continue;
X	}
X	if (!more_input) {
X	  already_continued = FALSE;
X	  clean_request (buf);
X	  if (!strncmp (buf, "quit", 4) || !strncmp (buf, "exit", 4))
X	    break;
X	  if (!strncmp (buf, "__abort__", 4)) /* Reserved */
X	    goto abort;
X# ifdef bsd
X	  sig_mask = sigblock (sigmask (SIGALRM));
X# elif defined (svr4) || defined (svr3)
X	  sighold (SIGALRM);
X# endif
X	  if (!strncmp (buf, "binary", 3)) {
X	    bin = TRUE;
X	    REPLY (msg_sock, OK, strlen (BINARY_XFER) + strlen (PROMPT));
X	    if (write_to_fd (msg_sock, BINARY_XFER, strlen (BINARY_XFER)) < 0)
X	      CLIENT_LOST (13);
X	    goto Skip;
X	  }
X	  if (!strncmp (buf, "ascii", 3)) {
X	    bin = FALSE;
X	    REPLY (msg_sock, OK, strlen (ASCII_XFER) + strlen (PROMPT));
X	    if (write_to_fd (msg_sock, ASCII_XFER, strlen (ASCII_XFER)) < 0)
X	      CLIENT_LOST (13);
X	    goto Skip;
X	  }
X	  if (buf [0] == '?' || !strncmp (buf, "privileges", 4)) {
X	    REPLY (msg_sock, OK, strlen (perms [auth]) + strlen (PROMPT));
X	    if (write_to_fd (msg_sock, perms [auth], strlen (perms [auth])) < 0)
X	      CLIENT_LOST (13);
X	    goto Skip;
X	  }
X	  if (!strncmp (buf, "timeleft", 4)) {
X	    sprintf (buf, "Time left: %d seconds\n", conn_duration - time (0) +
X		     time_started);
X	    REPLY (msg_sock, OK, strlen (buf) + strlen (PROMPT));
X	    if (write_to_fd (msg_sock, buf, strlen (buf)) < 0)
X	      CLIENT_LOST (13);
X	    goto Skip;
X	  }
X
X	  sprintf (requests_file, "/tmp/.live.%d", getpid());
X	  OPEN_FILE (f, requests_file, "w", "process_live_request");
X	  chown (requests_file, pwentry->pw_uid, pwentry->pw_gid);
X	  t = time (0);
X	  fprintf (f, "From %s %sComment: IUL connection from %s\nAccess: ",
X		   msg, ctime (&t),
X		   (hp ? hp->h_name : (char *) inet_ntoa (client.sin_addr)));
X	  if (auth == SUBSCRIBED)
X	    fprintf (f, "Subscriber to list %s", sys.lists[listid].alias);
X	  else if (auth == OWNER)
X	    fprintf (f, "Owner of list %s", sys.lists[listid].alias);
X	  else if (auth == MANAGER)
X	    fprintf (f, "Manager");
X	  else
X	    fprintf (f, "Casual");
X	  fprintf (f, "\n\n");
X
X	  put_request = FALSE;
X	  RESET (arg);
X	  sscanf (buf, "%s", arg);
X	  if (re_strcmp ("^PUT$|^PUT[ \t]", upcase (arg), NULL) > 0) {
X	    if (!check_for_redirection (buf, outfile, &fmode, msg_sock, bin,
X		FALSE)) {
X	      fclose (f);
X	      goto Skip;
X	    }
X	    if (outfile [0] != EOS &&
X		!writeable_file (outfile, msg_sock, FALSE)) {
X	      fclose (f);
X	      goto Skip;
X	    }
X	    REPLY (msg_sock, MORE_INPUT_REQUIRED, strlen (MORE_INPUT));
X	    if (write_to_fd (msg_sock, MORE_INPUT, strlen (MORE_INPUT)) < 0)
X	      CLIENT_LOST (13);
X	    more_input = TRUE;
X	    reply_code = FALSE;
X	    put_request = TRUE;
X	    if (auth >= SUBSCRIBED && (dash = _strstr (buf, " - ")))
X	      insert_word (dash + 1, &passwd, 1, 0, 1);
X	  }
X	}
X
X	if (!strcmp (buf, ".") && more_input)
X	  more_input = FALSE;
X	if (!strncmp (buf, "..", 2) && more_input)  /* Remove one dot */
X	  for (l = 0; l < strlen (buf); l++)
X	    buf [l] = buf [l + 1];
X
X	if (more_input && ch == '\n' && buf[0] == EOS)
X	  strcat (buf, " ");	/* Kludge */
X
X	if (buf[0] != EOS) { /* Not \n only */
X
X	  if (!more_input) {
X	    IN_CRITICAL_SECTION ("process_live_request", msg_sock);
X
X	    if (!put_request) { /* Redirection has been checked and verified */
X	      if (!check_for_redirection (buf, outfile, &fmode, msg_sock, bin,
X		  TRUE)) {
X	        fclose (f);
X		OUT_OF_CRITICAL_SECTION ("process_live_request");
X	        goto Skip;
X	      }
X	      RESET (arg);
X	      sscanf (buf, "%s", arg);
X              if (outfile [0] != EOS || !strcmp (upcase (arg), "GET")) {
X	        if (outfile [0] == EOS && !strcmp (arg, "GET"))
X		  sscanf (buf, "%s %s %s", arg, arg, outfile),
X		  fmode = (bin ? WRITE_TO_FILE_BIN : WRITE_TO_FILE_ASC);
X	        if (!writeable_file (outfile, msg_sock, TRUE)) {
X		  fclose (f);
X		  OUT_OF_CRITICAL_SECTION ("process_live_request");
X	          goto Skip;
X		}
X	      }
X	    }
X	    if (strcmp (buf, ".")) {
X	      if (auth >= SUBSCRIBED && (dash = _strstr (buf, " - ")))
X		insert_word (dash + 1, &passwd, 1, 0, 1);
X	      fprintf (f, "%s\n", buf);
X	    }
X	    fclose (f);
X	    cat_append (requests_file, LIVE_REQUESTS_F);
X	    chown (LIVE_REQUESTS_F, pwentry->pw_uid, pwentry->pw_gid);
X	    unlink (requests_file);
X
X	    sprintf (server, "%s -i %s %s", SERVER, sys.server.cmdoptions,
X		     denied_req [auth]);
X	    run_program (server, TRUE, msg_sock);
X	    if ((f = fopen (ULISTSERVER_REPLY, "r")) != NULL)
X	      fscanf (f, "%d", &rcode),
X	      fclose (f);
X	    else
X	      rcode = SYS_ERROR;
X	    if ((response = open (MAILFORWARD, O_RDONLY)) >= 0) {
X	      stat (MAILFORWARD, &stat_buf);
X	      nlines = 0;
X	      if (outfile [0] != EOS &&
X		  (rcode == WRITE_TO_FILE_ASC || rcode == WRITE_TO_FILE_BIN ||
X		   rcode == OK || rcode == RESTRICTED_REQ ||
X		   rcode == PERMISSION_DENIED)) {
X		if (!bin) { /* Count # of \n chars */
X		  bytes_read = 1;
X		  while (bytes_read > 0) {
X		    errno = 0;
X		    while ((bytes_read = read (response, buf, BUFFSIZ - 1)) < 0
X			   && (errno == EINTR
X#ifdef ERESTART
X			   || errno == ERESTART)
X#else
X			       )
X#endif
X			   )
X		      errno = 0;
X		    if (errno && errno != EINTR
X#ifdef ERESTART
X			&& errno != ERESTART
X#endif
X		      ) {
X		      OUT_OF_CRITICAL_SECTION ("process_live_request");
X		      CLOSE_CLIENT (msg_sock);
X# ifndef SYSLOG
X		      fclose (report);
X# endif
X
X		      _exit (13);
X		    }
X		    for (i = 0; i < bytes_read; ++i)
X		      if (buf[i] == '\n') ++nlines;
X		  }
X		  lseek (response, 0L, SEEK_SET);
X		}
X		REPLY_FILE (msg_sock, fmode,
X			    stat_buf.st_size + strlen (PROMPT) + nlines, 
X			    outfile)
X	      }
X	      else
X	        REPLY (msg_sock, rcode, stat_buf.st_size + strlen (PROMPT));
X	      bytes_read = 1;
X	      while (bytes_read > 0) {
X		errno = 0;
X		while ((bytes_read = read (response, buf, BUFFSIZ)) < 0
X		       && (errno == EINTR
X#ifdef ERESTART
X			   || errno == ERESTART)
X#else
X			   )
X#endif
X		      )
X		  errno = 0;
X		if (errno && errno != EINTR
X#ifdef ERESTART
X		    && errno != ERESTART
X#endif
X		    ) {
X		  OUT_OF_CRITICAL_SECTION ("process_live_request");
X		  CLOSE_CLIENT (msg_sock);
X# ifndef SYSLOG
X		  fclose (report);
X# endif
X		  _exit (13);
X		}
X		if (bytes_read > 0) {
X		  if (!bin && outfile[0] != EOS &&
X		      (rcode == WRITE_TO_FILE_ASC ||
X		       rcode == WRITE_TO_FILE_BIN ||
X		       rcode == OK || rcode == RESTRICTED_REQ ||
X		       rcode == PERMISSION_DENIED))
X		    for (i = 0; i < bytes_read; i++)
X		      if (buf [i] == '\n') {
X			if (bytes_read == buffsiz)
X			  if ((buf = (char *)
X			       realloc (buf, (++buffsiz) * sizeof (char))) ==
X			      NULL) {
X			    OUT_OF_CRITICAL_SECTION ("process_live_request");
X			    report_progress (report, "\nprocess_live_request():\
X realloc() failed", TRUE),
X			    exit (11);
X			  }
X			for (j = bytes_read; j > i; j--)
X			  buf [j] = buf [j - 1];
X			buf [i] = '\r';
X			++bytes_read;
X			++i;
X		      }
X		  if (write_to_fd (msg_sock, buf, bytes_read) < 0) {
X		    OUT_OF_CRITICAL_SECTION ("process_live_request");
X		    CLIENT_LOST (13);
X		  }
X		}
X	      }
X	      close (response);
X	      unlink (MAILFORWARD);
X	      unlink (ULISTSERVER_REPLY);
X	    }
X	    else
X	      REPLY (msg_sock, SYS_ERROR, strlen (PROMPT));
X	    RESET (outfile);
X	    OUT_OF_CRITICAL_SECTION ("process_live_request");
X	  }
X	  else {	/* More input */
X	    fprintf (f, "%s\n", buf);
X	    if (reply_code)	/* Kludge */
X	      REPLY (msg_sock, OK, 0);
X	    reply_code = TRUE;
X	  }
X	}
X	else { /* Empty request */
X	  fclose (f);
X	  REPLY (msg_sock, OK, strlen (PROMPT));
X	}
X      Skip:
X	if (!more_input)
X	  if (write_to_fd (msg_sock, PROMPT, strlen (PROMPT)) < 0)
X	    CLIENT_LOST (13);
X	bytes_read = 0;
X	ch = RESET (buf);
X# ifdef bsd
X	sigsetmask (sig_mask);
X# elif defined (svr4) || defined (svr3)
X	sigrelse (SIGALRM);
X# endif
X      }
X    }
X  }
X  abort:
X  REPLY (msg_sock, CONN_CLOSED, strlen (GOOD_BYE));
X  write_to_fd (msg_sock, GOOD_BYE, strlen (GOOD_BYE));
X  ciao:
X  free ((char *) buf);
X  free ((char *) passwd);
X  unlink (requests_file);
X  close (msg_sock);
X# ifndef SYSLOG
X  fclose (report);
X# endif
X  _exit (0);
X}
X
X/*
X  Check for redirection to a file; return TRUE if no errors, FALSE otherwise.
X  The file to written to (if any) will be stored in 'outfile' and 'fmode'
X  will be set.
X*/
X
XBOOLEAN check_for_redirection (char *buf, char *outfile, int *fmode,
Xint msg_sock, BOOLEAN bin, BOOLEAN in_critical_section)
X{
X  char *s = strrchr (buf, EOS), reply [MAX_LINE];
X  int i, nquote = 0;
X
X  RESET (outfile);
X  *fmode = 0;
X  while (s >= buf) {
X    if (!nquote) {
X      if (*s == '\'' && (s == buf || *(s - 1) != '\\')) /* Count single quotes */
X	nquote = 1;
X      else if (*s == '"' && (s == buf || *(s - 1) != '\\')) /* Count double quotes */
X	nquote = 2;
X      else if (*s == '>')
X	if (s != buf && *(s - 1) == '\\')
X	  sprintf (s - 1, "%s", s), /* Remove \ */
X	  --s;
X	else if (outfile[0] == EOS) {
X	  sscanf (s + 1, "%s", outfile);
X	  if (outfile [0] == EOS) {
X	    REPLY (msg_sock, SYNTAX_ERROR, strlen (NULL_REDIRECT) +
X		   strlen (PROMPT));
X	    i = write_to_fd (msg_sock, NULL_REDIRECT, strlen (NULL_REDIRECT));
X	    if (in_critical_section && V (sid) < 0) { /* Out of critical section */
X	      report_progress (report, "\ncheck_for_redirection(): \
XError V()", TRUE);
X# ifndef SYSLOG
X	      fclose (report);
X# endif
X	      CLOSE_CLIENT (msg_sock);
X	      _exit (14);
X	    }
X	    if (i < 0)
X	      CLIENT_LOST (13);
X	    return FALSE;
X	  }
X	  else {
X	    *fmode = (bin ? WRITE_TO_FILE_BIN : WRITE_TO_FILE_ASC);
X	    if (*(s - 1) == '>') {
X	      if (s - 1 != buf && *(s - 2) == '\\')
X		sprintf (s - 2, "%s", s - 1);
X	      else
X		*fmode = (bin ? APPEND_TO_FILE_BIN : APPEND_TO_FILE_ASC);
X	      --s;
X	    }
X	  }
X	  *s = EOS;
X	}
X    }
X    else if ((nquote == 1 && *s == '\'' &&
X	      (s != buf ? *(s - 1) != '\\' : 1)) ||
X	     (nquote == 2 && *s == '"' && 
X	      (s != buf ? *(s - 1) != '\\' : 1)))
X      nquote = 0;
X    --s;
X  }
X  return TRUE;
X}
X
X/*
X  Return TRUE if the file is writeable on the client side, FALSE otherwise.
X*/
X
XBOOLEAN writeable_file (char *outfile, int msg_sock,
X			BOOLEAN in_critical_section)
X{
X  char reply [MAX_LINE];
X  int i = 0, err;
X
X  REPLY_FILE (msg_sock, TEST_FILE_PERMISSIONS, 0, outfile);
X  errno = 0;
X  while (read (msg_sock, reply, 3) < 0 && (errno == EINTR
X#ifdef ERESTART
X	 || errno == ERESTART)
X#else
X					   )
X#endif
X	 ) {
X    i = sizeof (err);
X    if (!getsockopt (sock_fd, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &i)) {
X      i = 0;
X      if (err) {
X	if (in_critical_section && V (sid) < 0) /* Out of critical section */
X	  report_progress (report, "\nwriteable_file(): Error V()", TRUE),
X	  i = 1;
X	CLIENT_LOST (13 + i);
X      }
X    }
X    else {
X      REPLY (msg_sock, SYS_ERROR, strlen (REMOTE_SYS_ERROR));
X      write_to_fd (msg_sock, REMOTE_SYS_ERROR, strlen (REMOTE_SYS_ERROR));
X      CLIENT_LOST (13);
X    }
X    errno = 0;
X  }
X  if (errno && errno != EINTR
X#ifdef ERESTART
X      && errno != ERESTART
X#endif
X      ) {
X    if (in_critical_section && V (sid) < 0) /* Out of critical section */
X      report_progress (report, "\nwriteable_file(): Error V()", TRUE),
X      i = 1;
X    CLIENT_LOST (13 + i);
X  }
X  reply [3] = EOS;
X  if (atoi (reply) == PERMISSION_DENIED) {
X    REPLY (msg_sock, OK, strlen (PROMPT));
X    RESET (outfile);
X    if (in_critical_section && V (sid) < 0) { /* Out of critical section */
X      report_progress (report, "\nwriteable_file(): Error V()", TRUE);
X# ifndef SYSLOG
X      fclose (report);
X# endif
X      CLOSE_CLIENT (msg_sock);
X      _exit (14);
X    }
X    return FALSE;
X  }
X  return TRUE;
X}
X
X/*
X  Return TRUE id 'pid' in in 'clients', FALSE otherwise.
X*/
X
XBOOLEAN child_alive (int pid, CLIENT clients [])
X{
X  int i;
X
X  for (i = 0; i < nforks; i++)
X    if (clients[i].pid == pid)
X      return TRUE;
X  return FALSE;
X}
X#endif
*-*-END-of-src/serverd.c-*-*
echo x - src/signals.c
sed 's/^X//' >src/signals.c <<'*-*-END-of-src/signals.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                            SIGNAL FUNCTIONS                              |
X  |                                                                          |
X  |                              Version 2.2                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <signal.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include "defs.h"
X#include "struct.h"
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xextern int  syscom ();
Xextern char *tsprintf ();
X#endif
Xextern void report_progress (FILE *, char *, int);
Xextern char *extract_filename (char *);
Xextern int  gexit (int);
X
Xvoid   init_signals (void);
Xvoid   catch_signals (void);
Xvoid   my_abort (int);
X
Xstatic char *signals[MAX_SIGNAL + 1];   /* Signal names */
X
X/*
X  Initialize the signals[].
X*/
X
Xvoid init_signals ()
X{
X  signals[SIGILL] = "SIGILL";
X  signals[SIGQUIT] = "SIGQUIT";
X  signals[SIGTERM] = "SIGTERM";
X  signals[SIGBUS] = "SIGBUS";
X  signals[SIGSEGV] = "SIGSEGV";
X  signals[SIGKILL] = "SIGKILL";
X}
X
X/*
X  Catch signals to print messages before aborting.
X*/
X
Xvoid catch_signals ()
X{
X#ifdef _AIX
X  struct sigaction s;
X#endif
X  
X#ifdef SIGTSTP
X  signal (SIGTSTP, SIG_IGN);
X#endif
X#ifdef SIGTTIN
X  signal (SIGTTIN, SIG_IGN);
X#endif
X#ifdef SIGTTOU
X  signal (SIGTTOU, SIG_IGN);
X#endif
X  signal (SIGHUP, SIG_IGN);
X  signal (SIGILL, my_abort);
X  signal (SIGQUIT, my_abort);
X  signal (SIGTERM, my_abort);
X  signal (SIGSEGV, my_abort);
X  signal (SIGBUS, my_abort);
X  signal (SIGKILL, my_abort); /* Can't be caught, but why not try? */
X#ifdef _AIX
X  s.sa_handler = my_abort;
X  s.sa_mask.losigs = s.sa_mask.hisigs = 0;
X  s.sa_flags = SA_FULLDUMP;
X  sigaction (SIGSEGV, &s, (struct sigaction *) NULL);
X  sigaction (SIGILL, &s, (struct sigaction *) NULL);
X  sigaction (SIGQUIT, &s, (struct sigaction *) NULL);
X  sigaction (SIGBUS, &s, (struct sigaction *) NULL); 
X#endif
X}
X
X/*
X  Kill program after sending message to MANAGER that this is
X  about to happen (message is sent only when using UCB mail).
X*/
X
Xvoid my_abort (int sig)
X{
X  extern SYS sys;
X  extern char *prog;
X  extern FILE *report;
X  char *s;
X
X  signal (sig, SIG_IGN);
X  report_progress (report, tsprintf ("\n*** %s: Received %s signal ***\n", prog,
X		   signals[sig]), -TRUE);
X  if (sys.options & BSD_MAIL)
X    syscom ("echo '' | %s -s '%s received %s' %s &", UCB_MAIL,
X	    (prog ? prog : "???"), signals[sig], sys.manager);
X  if (sig == SIGQUIT || sig == SIGILL || sig == SIGSEGV || sig == SIGBUS) {
X    if (prog)
X      s = extract_filename (prog),
X#ifdef __STDC__
X      syscom ("rm -f %s/core; touch %s/core.%s; ln -s %s/core.%s %s/core",
X	      PATH, PATH, s, PATH, s, PATH),
X#else
X      syscom ("rm -f %s/core; touch %s/core.%s; ln -s %s/core.%s %s/core",
X	      PATH", PATH", s, PATH", s, PATH"),
X#endif
X      abort ();
X  }
X  gexit (8);
X}
*-*-END-of-src/signals.c-*-*
echo x - src/siul.c
sed 's/^X//' >src/siul.c <<'*-*-END-of-src/siul.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                 SYSTEM INTERACTIVE UNIX-LISTSERVER CLIENT		     |
X  |									     |
X  |				  Version 1.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.		     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  This is a copy of the iul client (iul.c) ported for internal use by listserv
X  for connections to remote servers while processing requests for remote lists.
X
X  Refer to the man page (iul.1) or iul.c for more information.
X
X  PLEASE DO NOT MODIFY THESE ROUTINES AS YOU MAY BREAK THE PROTOCOL AND CREATE
X  HAVOC WITH PEER INTERACTIVE UNIX LISTSERVERS. A LOT OF UNDOCUMENTED
X  ASSUMPTIONS ARE MADE. YOU SHOULD CONTACT ME ABOUT ANY CHANGES YOU WISH TO
X  MAKE.
X*/
X
X#include <signal.h>
X#include "defs.h"
X#undef	 NSIG
X#ifdef GO_INTERACTIVE
X# include <stdio.h>
X# include <sys/types.h>
X# include <string.h>
X# if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X# endif
X# ifndef unknown_port
X#  ifndef __NeXT__
X#   include <unistd.h>
X#  else
X#   include <libc.h>
X#  endif
X# endif
X# if defined (stardent) || defined (stellar) || defined (titan)
X#  include <rpc/types.h>
X# endif
X# include <sys/stat.h>
X# include <fcntl.h>
X# include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X# include <sys/socket.h>
X# include <netinet/in.h>
X# include <netdb.h>
X# include <sys/file.h>
X# include <sys/ioctl.h>
X# if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X#  include <sys/stropts.h>
X# endif
X# if (defined (sco) || defined (M_XENIX) || defined (M_UNIX)) && \
X  defined (HAVE_SELECT_H)
X#  include <sys/times.h>
X# else
X#  include <sys/time.h>
X# endif
X# ifdef HAVE_SETJMP_H
X#  include <setjmp.h>
X# endif
X# ifdef HAVE_SELECT_H
X#  include <sys/select.h>
X# endif
X# ifdef HAVE_ULIMIT_H
X#  include <ulimit.h>
X# endif
X# include "iul.h"
X
X# ifndef UL_GDESLIM
X#  define UL_GDESLIM	4
X# endif
X
X# ifndef FD_SET          /* for 4.2BSD */
X#  define FD_SETSIZE      (sizeof(fd_set) * 8)
X#  define FD_SET(n, p)    (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
X#  define FD_CLR(n, p)    (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
X#  define FD_ISSET(n, p)  (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
X#  define FD_ZERO(p)      memset ((char *)(p), '\0', sizeof(*(p)))
X# endif
X
X# ifndef __NeXT__
Xextern	long int atoi (char *);
X# else
Xextern	int atoi (const char *);
X# endif
Xextern  long int write_to_fd (int, char *, long int);
Xextern  void report_progress (FILE *, char *, BOOLEAN);
Xextern  char *upcase (char *);
Xextern	char *_strstr (char *, char *);
X
Xextern  FILE *report;
X
Xint	 iul_server_response (int, int);
Xint      connect_to_iul_server (char *, int);
Xint	 sighandle (int);
Xint	 urg_data (int);
Xlong int read_from_fd (int, long int, int);
Xint	 open_file (char *, int, int);
Xint 	 check_server_response (int, char *, int);
Xint	 check_for_redirection (char *, char *);
X
Xlong int nbytes, sock_fd;
Xint  to_file, prompt_len, ofd, check_sio, append_output = 0;
Xchar args [256];
Xvoid (*f1)(), (*f2)(), (*f3)();
X# ifdef HAVE_SETJMP_H
Xjmp_buf env;
X# endif
X
X#else
X# include "iulp.h"
X#endif
X
X#ifdef __STDC__
X# include <stdarg.h>
Xint     siul (char *, int, char *, char *, int, char *, char *, ...);
Xextern  char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xint     siul ();
Xextern  char *tsprintf ();
X#endif
X
X/*
X  Main function. Connect to server, issue requests and receive replies.
X
X  Returns: an IULP code or -1 on error.
X*/
X
X#ifdef __STDC__
Xint siul (char *Host, int port, char *Login, char *Passwd,
X	  int response_timeout, char *outfile, char *control, ...)
X#else
Xint siul (Host, port, Login, Passwd, response_timeout, outfile, control,
X	  va_alist)
Xchar *Host, *outfile, *Login, *Passwd, *control;
Xint port, response_timeout;
Xva_dcl
X#endif
X{
X#ifndef GO_INTERACTIVE
X  return SYS_ERROR;
X#else
X  char *buf, *tmp, infile [256], wbuf [256], *sep = "IUL:", *s, *r, rhost [256];
X  char login [256], passwd [256], host [256], raddr [256], taddr [256];
X  char version [80];
X  int cmd, i, sprompt_len, timeout, rfd, bytes_alloced = 0, naddr, naddr2,
X    _prompt_len;
X  struct stat stat_buf;
X  struct hostent *hp, *thp;
X  struct in_addr _addr;
X  va_list ap;
X
X
X  check_sio = 0;
X  strncpy (login, Login, 255);
X  strncpy (passwd, Passwd, 255);
X  strncpy (host, Host, 255);
X  login [255] = passwd [255] = host [255] = EOS;
X  if ((ofd = open (outfile, O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0) {
X    report_progress (report, tsprintf ("\nsiul(): Cannot open %s", outfile),
X		     TRUE);
X    return -1;
X  }
X  bytes_alloced = 256;
X  if ((buf = (char *) malloc (bytes_alloced * sizeof (char))) == NULL) {
X    report_progress (report, "\nsiul(): malloc() failed", TRUE);
X    close (ofd);
X    return -1;
X  }
X
X# ifdef HAVE_SETJMP_H
X#  ifdef SIGIO
X  f1 = (void (*)()) signal (SIGIO, (void (*)()) urg_data);
X#  endif
X#  ifdef SIGURG
X  f2 = (void (*)()) signal (SIGURG, (void (*)()) urg_data);
X#  endif
X  f3 = (void (*)()) signal (SIGPIPE, (void (*)()) sighandle);
X# endif
X# ifdef __STDC__
X  va_start (ap, control);
X# else
X  va_start (ap);
X# endif
X
X  upcase (login);
X  s = login;
X  i = 0;
X  while (i < MAX_HOPS && (s = _strstr (s, sep)))
X    ++i,
X    ++s;
X  if (i >= MAX_HOPS) {
X    write_to_fd (ofd, "Too many hops.\n", 15);
X    cmd = PEER_UNAVAIL;
X    goto abort;
X  }
X  s = login;
X  upcase (host);
X  while ((s = _strstr (s, sep))) {	/* Check for loop */
X    s += 4;   /* Point to remote host */
X    sscanf (s, "%s", rhost);
X    if ((r = strchr (rhost, ';')))
X      *r = EOS;
X    thp = hp = gethostbyname (host);
X    if (hp) { /* Just got IP address(es) of host */
X      if ((thp = (struct hostent *) malloc (sizeof (struct hostent))) == NULL) {
X	report_progress (report, "\nsiul(): malloc() failed", TRUE);
X	close (ofd);
X	return -1;
X      }
X      memcpy ((char *) thp, (char *) hp, sizeof (*hp));
X#ifdef h_addr
X      naddr = 0;
X      if ((thp->h_addr_list = (char **) malloc (sizeof (char *))) == NULL) {
X	report_progress (report, "\nsiul(): malloc() failed", TRUE);
X	close (ofd);
X	return -1;
X      }
X      while (hp->h_addr_list[naddr]) { /* Copy all IP addresses */
X	if ((thp->h_addr_list = (char **)
X	     realloc (thp->h_addr_list, (naddr + 2) * sizeof (char *))) == NULL) {
X	  report_progress (report, "\nsiul(): realloc() failed", TRUE);
X	  close (ofd);
X	  return -1;
X	}
X	if ((thp->h_addr_list[naddr] =
X	     (char *) malloc (thp->h_length * sizeof (char))) == NULL) {
X	  report_progress (report, "\nsiul(): malloc() failed", TRUE);
X	  close (ofd);
X	  return -1;
X	}
X	memcpy ((char *) thp->h_addr_list[naddr],
X		(char *) hp->h_addr_list[naddr], hp->h_length);
X	thp->h_addr_list[++naddr] = NULL;
X      }
X      naddr = 0;
X      while (thp->h_addr_list[naddr]) { /* For all target addresses */
X	memcpy ((char *) &_addr, (char *) thp->h_addr_list[naddr++],
X		thp->h_length);
X	strcpy (taddr, (char *) inet_ntoa (_addr));
X	hp = gethostbyname (rhost);
X	naddr2 = 0;
X	while (hp && hp->h_addr_list[naddr2]) { /* Check all hosts visited */
X	  memcpy ((char *) &_addr, (char *) hp->h_addr_list[naddr2++],
X		  hp->h_length);
X	  strcpy (raddr, (char *) inet_ntoa (_addr));
X	  if (!strcmp (taddr, raddr)) { /* Loop */
X	    write_to_fd (ofd, "Loop detected.\n", 15);
X	    cmd = PEER_UNAVAIL;
X	    goto abort;
X	  }
X	}
X	/* Assume gethostbyname() failed */
X	if (!strcmp (host, rhost) || !strcmp (taddr, rhost)) { /* Loop !!! */
X	  write_to_fd (ofd, "Loop detected.\n", 15);
X	  cmd = PEER_UNAVAIL;
X	  goto abort;
X	}
X      }
X      naddr = 0;
X      while (thp->h_addr_list[naddr])
X	free ((char *) thp->h_addr_list[naddr++]);
X      free ((char **) thp->h_addr_list);
X#else
X      if ((thp->h_addr = (char *) malloc ((strlen (hp->h_addr) + 1) *
X					  sizeof (char))) == NULL) {
X	report_progress (report, "\nsiul(): malloc() failed", TRUE);
X	close (ofd);
X	return -1;
X      }
X      memcpy ((char *) thp->h_addr, (char *) hp->h_addr, hp->h_length);
X      memcpy ((char *) &_addr, (char *) thp->h_addr, thp->h_length);
X      strcpy (taddr, (char *) inet_ntoa (_addr));
X      hp = gethostbyname (rhost);
X      memcpy ((char *) &_addr, (char *) hp->h_addr, hp->h_length);
X      strcpy (raddr, (char *) inet_ntoa (_addr));
X      if (!strcmp (taddr, raddr)) { /* Loop */
X	write_to_fd (ofd, "Loop detected.\n", 15);
X	cmd = PEER_UNAVAIL;
X	goto abort;
X      }
X      free ((char *) thp->h_addr);
X#endif
X      free ((struct hostent *) thp);
X    }
X    else { /* Host is an IP address */
X      naddr = 0;
X      hp = gethostbyname (rhost);
X#ifdef h_addr
X      while (hp && hp->h_addr_list[naddr]) { /* Check all hosts visited */
X	memcpy ((char *) &_addr, (char *) hp->h_addr_list[naddr++],
X		hp->h_length);
X	strcpy (raddr, (char *) inet_ntoa (_addr));
X	if (!strcmp (raddr, host)) { /* Loop */
X	  write_to_fd (ofd, "Loop detected.\n", 15);
X	  cmd = PEER_UNAVAIL;
X	  goto abort;
X	}
X      }
X#else
X      memcpy ((char *) &_addr, (char *) hp->h_addr, hp->h_length);
X      strcpy (raddr, (char *) inet_ntoa (_addr));
X      if (!strcmp (raddr, host)) { /* Loop */
X	write_to_fd (ofd, "Loop detected.\n", 15);
X	cmd = PEER_UNAVAIL;
X	goto abort;
X      }
X#endif
X    }
X  }
X
X  if ((sock_fd = connect_to_iul_server (host, port)) < 0)
X    if ((sock_fd = connect_to_iul_server (host, (((port & 0xff00) >> 8) |
X	 ((port & 0xff) << 8)))) < 0) {
X      write_to_fd (ofd, "Could not connect to host.\n", 27);
X      cmd = PEER_UNAVAIL;
X      goto abort;
X    }
X
X  if ((cmd = iul_server_response (sock_fd, response_timeout)) != CONNECT) {
X    write_to_fd (ofd, "Handshake failed, not an interactive UNIX ListServer \
Xserver.\n", 59);
X    goto abort;
X  }
X
X# ifdef HAVE_SETJMP_H
X  if (!setjmp (env)) {	/* No signal; proceed */
X# endif
X
X  if (check_server_response (cmd, args, response_timeout))
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  if ((timeout = nbytes) < response_timeout)
X    response_timeout = timeout;
X  sscanf (args, "%s %d %d\n", version, &_prompt_len, &sprompt_len);
X
X  if ((cmd = iul_server_response (sock_fd, response_timeout)) < 0)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  check_server_response (cmd, args, response_timeout);
X  if (cmd == CONN_CLOSED || cmd == SERVER_BUSY || cmd == PEER_UNAVAIL ||
X      cmd == CONN_ABORTED || cmd == SYS_ERROR || cmd == CONN_TIMEOUT)
X    append_output = 1;
X  if (read_from_fd (sock_fd, nbytes, response_timeout) < 0)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  if (cmd == CONN_CLOSED || cmd == SERVER_BUSY || cmd == PEER_UNAVAIL ||
X      cmd == CONN_ABORTED || cmd == SYS_ERROR || cmd == CONN_TIMEOUT)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  prompt_len = _prompt_len;
X  strcat (login, "\n");
X  if (write_to_fd (sock_fd, login, strlen (login)) < 0)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  if ((cmd = iul_server_response (sock_fd, response_timeout)) < 0)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  if (check_server_response (cmd, args, response_timeout))
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X  if (cmd == PASSWORD_REQUIRED) {
X    strcat (passwd, "\n");
X    if (write_to_fd (sock_fd, passwd, strlen (passwd)) < 0)
X# ifdef HAVE_SETJMP_H
X      longjmp (env, 1);
X# else
X      goto abort;
X# endif
X  }
X  else if (cmd == PEER_UNAVAIL)
X# ifdef HAVE_SETJMP_H
X    longjmp (env, 1);
X# else
X    goto abort;
X# endif
X
X  RESET (buf);
X  vsprintf (buf, control, ap);
X  va_end (ap);
X  strcat (buf, "\nquit\n");	/* Just in case */
X  while (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT) {
X    check_sio = 0;
X    if ((cmd = iul_server_response (sock_fd, response_timeout)) < 0) break;
X    if (check_server_response (cmd, args, response_timeout)) break;
X    if (cmd == TEST_FILE_PERMISSIONS) continue;
X    if (cmd != CONN_ABORTED && cmd != CONN_TIMEOUT)
X      if (read_from_fd (sock_fd, nbytes, response_timeout) < 0) break;
X    check_sio = ! (append_output = 0);
X    if (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT) {
X      if (buf [0] == EOS) {
X        RESET (infile);
X        if ((rfd = check_for_redirection (buf, infile)) < 0)
X	  strcpy (buf, "\n");
X        if (rfd >= 0 && infile [0] != EOS) { /* Input redirection */
X	  fstat (rfd, &stat_buf);
X	  if (!(stat_buf.st_mode & S_IFREG)) {
X	    printf ("%s: Not a regular file\n", infile);
X	    strcpy (buf, "\n");
X	    goto skip;
X	  }
X	  if ((stat_buf.st_size + strlen (buf) + 2) > bytes_alloced) {
X	    bytes_alloced = stat_buf.st_size + strlen (buf) + 2;
X	    if ((buf = (char *) realloc (buf, bytes_alloced * sizeof (char))) ==
X	        NULL) {
X	      write_to_fd (ofd, "FATAL: internal memory error.\n", 30);
X	      report_progress (report, "\nsiul(): realloc() failed", TRUE);
X	      cmd = -1;
X	      break;
X	    }
X	  }
X	  i = strlen (buf);
X	  if (read (rfd, &buf [strlen (buf)], stat_buf.st_size) <
X	      stat_buf.st_size) {
X	    write_to_fd (ofd, "FATAL: Failed to read all of the input \
Xfile.\n", 45);
X	    cmd = -1;
X	    break;
X	  }
X	  buf [i + stat_buf.st_size] = EOS;
X	  if (buf [i + stat_buf.st_size - 1] != '\n')
X	    strcat (&buf [i + stat_buf.st_size], "\n");
X	  close (rfd);
X        }
X      }
X      skip:
X      strncpy (wbuf, buf, 174);
X      while (!(tmp = strchr (wbuf, '\n')))
X	wbuf [174] = '\n';
X      if (tmp != wbuf) /* Not an empty line */
X        sprintf (tmp, " >> %s\n", outfile);
X      if (write_to_fd (sock_fd, wbuf, strlen (wbuf)) < 0) break;
X      sprintf (buf, "%s", strchr (buf, '\n') + 1);
X      if (strncmp (wbuf, "quit", 4) && strncmp (wbuf, "exit", 4) &&
X	  strncmp (wbuf, "__abort__", 9))
X	append_output = 1;
X    }
X  }
X
X# ifdef HAVE_SETJMP_H
X  } /* if (!setjmp (env)) ... */
X  else { /* Return from longjmp () */
X  }
X# endif
X
X abort:
X# ifdef HAVE_SETJMP_H
X#  ifdef SIGIO
X  signal (SIGIO, (void (*)()) f1);
X#  endif
X#  ifdef SIGURG
X  signal (SIGURG, (void (*)()) f2);
X#  endif
X  signal (SIGPIPE, (void (*)()) f3);
X# endif
X  if (sock_fd >= 0)
X    close (sock_fd);
X  close (ofd);
X  if (buf)
X    free ((char *) buf);
X  return cmd;
X#endif
X}
X
X#ifdef GO_INTERACTIVE
X/*
X  Get server response. Store in 'nbytes' the number of bytes in the actual
X  message that follows, and in 'args' the (optional) file name.
X
X  Returns: the server reply code, CONN_TIMEOUT, CONN_ABORTED, or -1 on 
X  protocol errors.
X*/
X
Xint iul_server_response (int sock_fd, int response_timeout)
X{
X  char buf [256];
X  int cmd = -1, i, bytes_to_read = 4, bytes_read = 0, err, l = sizeof (int);
X  long int time_started;
X
X  time_started = time (0);
X  memset (buf, EOS, 256);
X  while (bytes_to_read) {
X    errno = 0;
X    /* Get server command */
X    bytes_read = read (sock_fd, &buf [4 - bytes_to_read], bytes_to_read);
X    if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	errno == ECONNREFUSED) {
X      nbytes = 0;
X      return CONN_ABORTED;
X    }
X    if (!getsockopt (sock_fd, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l)) {
X      if (err || buf [4 - bytes_to_read] == EOF) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X    }
X    else {
X      nbytes = 0;
X      return SYS_ERROR;
X    }
X    if (errno || bytes_read < 1)
X      if ((time (0) - time_started) > response_timeout) {
X	nbytes = 0;
X	return CONN_TIMEOUT;
X      }
X    if (bytes_read > 0)
X      bytes_to_read -= bytes_read;
X  }
X  buf[3] = EOS;
X  cmd = atoi (buf);
X  i = 0;
X  do {		/* Get # of bytes in actual message */
X    again1:
X    if (read (sock_fd, &buf[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	  errno == ECONNREFUSED) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X	if ((time (0) - time_started) > response_timeout) {
X	  nbytes = 0;
X	  return CONN_TIMEOUT;
X	}
X	goto again1;
X      }
X      write_to_fd (ofd, "Protocol error in control string.\n", 34);
X      return -1;
X    }
X    i++;
X  } while (buf[i - 1] != ' ');
X
X  buf[i - 1] = EOS;
X  nbytes = atoi (buf);
X  RESET (args);
X  i = 0;
X  do {		/* Get filename (optional) */
X    again2:
X    if (read (sock_fd, &args[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X          errno == ECONNREFUSED) {
X	nbytes = 0;
X        return CONN_ABORTED;
X      }
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X        if ((time (0) - time_started) > response_timeout) {
X          nbytes = 0;
X          return CONN_TIMEOUT;
X        }
X        goto again2;
X      }
X      write_to_fd (ofd, "Protocol error in control string.\n", 34);
X      return -1;
X    }
X    ++i;
X  } while (args[i - 1] != '\n');
X  args[i - 1]= EOS;
X  return cmd;
X}
X
X/*
X  Establish connection with unix-listserver. The socket is marked as
X  non-blocking.
X
X  Returns: the socket file descriptor, or -1 on error.
X*/
X
Xint connect_to_iul_server (char *host, int port)
X{
X  int sock_fd, sendbuf = BUFFSIZ, recvbuf = BUFFSIZ, value = 1, nfds, naddr = 0;
X  struct sockaddr_in sin;
X  struct hostent *hostentry;
X  struct timeval timeout;
X  fd_set readfds, writefds;
X 
X  timeout.tv_sec = timeout.tv_usec = 0;
X  if (!(hostentry = gethostbyname (host))) { /* Host name failed; try IP */
X    sin.sin_addr.s_addr = inet_addr (host);
X    if (! (hostentry = gethostbyaddr ((char *) &sin.sin_addr,
X				      sizeof (struct in_addr),
X				      AF_INET))) {
X      write_to_fd (ofd, "Invalid host.\n", 14);
X      return -1;
X    }
X  }
X
X  do {
X    if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X      report_progress (report, "\nconnect_to_iul_server(): Could not create \
Xsocket", TRUE);
X      return -1;
X    }
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		    sizeof (sendbuf)) < 0)
X      report_progress (report, "\nconnect_to_iul_server(): WARNING: Could not \
Xset send-buffer size: setsockopt() error", TRUE);
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		    sizeof (recvbuf)) < 0)
X      report_progress (report, "\nconnect_to_iul_server(): WARNING: Could not \
Xset receive-buffer size: setsockopt() error", TRUE);
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value,
X		    sizeof (value)) < 0)
X      report_progress (report, "\nconnect_to_iul_server(): WARNING: Cannot \
Xtoggle keep-alive connections: setsockopt() error", TRUE);
X
X#ifdef h_addr
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr_list[naddr++],
X	    hostentry->h_length);
X#else
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr,
X	    hostentry->h_length);
X#endif
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons (port);
X    memset (sin.sin_zero, EOS, sizeof (sin.sin_zero));
X    if (connect (sock_fd, (struct sockaddr *) &sin,
X		 sizeof (struct sockaddr_in)) < 0) {
X      if (errno != EINPROGRESS) {
X#ifdef h_addr
X	if (!hostentry->h_addr_list[naddr]) {
X#endif
X	  close (sock_fd);
X	  return -1;
X#ifdef h_addr
X	}
X	else {
X	  close (sock_fd);
X	  continue;
X	}
X#endif
X      }
X      FD_ZERO (&readfds);
X      FD_ZERO (&writefds);
X      do {
X	FD_SET (sock_fd, &readfds);
X	FD_SET (sock_fd, &writefds);
X	errno = 0;
X# if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X  defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X# else
X	nfds = ulimit (UL_GDESLIM);
X# endif
X	value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds, NULL,
X			&timeout);
X      } while (value == -1 && errno == EINTR);
X
X      if (value < 0) {
X	close (sock_fd);
X	return -1;
X      }
X      break;	/* Successful connection */
X    }
X    else
X      break;
X  } while (007);
X# ifdef NONBLOCKING_IO
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | O_NDELAY)) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Could not set \
Xnon-blocking I/O", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X# endif
X# ifdef HAVE_SETJMP_H
X#  if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X  if (ioctl (sock_fd, I_SETSIG, S_INPUT) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Cannot set SIGIO",
X		     TRUE);
X    close (sock_fd);
X    return -1;
X  }
X#  else
X#   ifdef F_SETOWN
X  if (fcntl (sock_fd, F_SETOWN, getpid()) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Cannot assign \
Xsocket to process group", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X#   elif defined (SIOCSPGRP)
X  value = -getpid();
X  if (ioctl (sock_fd, SIOCSPGRP, (char *) &value) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Cannot assign \
Xsocket to process group", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X#   else
X  report_progress (report, "\nconnect_to_iul_server(): Cannot assign \
Xsocket to process group", TRUE);
X  close (sock_fd);
X  return -1;
X#   endif
X#   ifdef FASYNC
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | FASYNC)) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Cannot set \
Xasynchronous I/O for socket", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X#   elif defined (FIOASYNC)
X  value = 1;
X  if (ioctl (sock_fd, FIOASYNC, (char *) &value) < 0) {
X    report_progress (report, "\nconnect_to_iul_server(): Cannot set \
Xasynchronous I/O for socket", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X#   else
X  report_progress (report, "\nconnect_to_iul_server(): Cannot set \
Xasynchronous I/O for socket", TRUE);
X  close (sock_fd);
X  return -1;
X#   endif
X#  endif
X# endif
X  return sock_fd;
X}
X
X# ifdef HAVE_SETJMP_H
X/*
X  Handle signals; the client sends an '__abort__' command and returns (long
X  jump); '__abort__' causes the other end to shut down.
X*/
X
Xint sighandle (int sig)
X{
X  check_sio = 0;
X  write (sock_fd, "__abort__\n", 10);
X  longjmp (env, 1);
X}
X
X/*
X  Urgent data from server; read it and return. Urgent data is received
X  when a connection is timed out or when the server shuts down.
X*/
X
Xint urg_data (int sig)
X{
X  int cmd;
X
X  if (!check_sio) {
X    signal (sig, (void (*)()) urg_data);
X    return;
X  }
X  signal (sig, SIG_IGN);
X  if ((cmd = iul_server_response (sock_fd, 30)) >= 0)
X    if (cmd == CONN_ABORTED || cmd == CONN_TIMEOUT || cmd == CONN_CLOSED)
X      check_server_response (cmd, args, 30),
X      append_output = 1,
X      read_from_fd (sock_fd, nbytes, 30);
X  longjmp (env, 1);
X}
X# endif
X
X/*
X  Read from a socket and write to ofd.
X
X  Returns: the actual number of bytes read on succes, or the negative of
X  that number (or -1) on error.
X*/
X
Xlong int read_from_fd (int rfd, long int bytes_to_read, int response_timeout)
X{
X  long int bytes_read, total_bytes = 0, time_started;
X  char buf [BUFFSIZ];
X
X  time_started = time (0);
X  if (bytes_to_read <= 0)
X    return 0;
X  errno = 0;
X  while ((bytes_read = read (rfd, buf, MIN (bytes_to_read, BUFFSIZ))) <
X	 bytes_to_read) {
X    if (bytes_read < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN &&
X	errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      sprintf (error, "\nread_from_fd(): ");
X      switch (errno) {
X	case EBADF: strcat (error, "Bad file number"); break;
X	case EFAULT: strcat (error, "Bad address"); break;
X	case EFBIG: strcat (error, "File limit reached"); break;
X	case EINVAL: strcat (error, "Negative seek pointer"); break;
X	case EIO: strcat (error, "I/O error"); break;
X	case ENOSPC: strcat (error, "No space left on device"); break;
X	case ENXIO: strcat (error, "No such device or address"); break;
X	case ERANGE: sprintf (error + strlen (error), "Bytes to read (%d) \
Xout of range", bytes_to_read); break;
X	default: sprintf (error + strlen (error), "Error number %d", errno);
X      }
X      report_progress (report, error, TRUE);
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    else if ((time (0) - time_started) > response_timeout)
X      return (total_bytes > 0 ? -total_bytes : -1);
X    if (bytes_read > 0) {
X      total_bytes += bytes_read;
X      bytes_to_read -= bytes_read;
X      if (to_file) {
X	if (bytes_to_read < prompt_len)
X	  to_file = 0;
X	if (write_to_fd (ofd, buf,
X			 (bytes_to_read < prompt_len ? 
X			  bytes_read - prompt_len + bytes_to_read :
X			  bytes_read)) < 0)
X	  return (total_bytes > 0 ? -total_bytes : -1);
X      }
X      else if (append_output)
X	if (write_to_fd (ofd, buf,
X			 (bytes_to_read < prompt_len ? 
X			  bytes_read - prompt_len + bytes_to_read :
X			  bytes_read)) < 0)
X	  return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    errno = 0;
X  }
X  if (bytes_read > 0) {
X    total_bytes += bytes_read;
X    if ((append_output || to_file) && bytes_read > prompt_len)
X      if (write_to_fd (ofd, buf, bytes_read - prompt_len) < 0)
X	return -1;
X  }
X  to_file = 0;
X  return total_bytes;
X}
X
X/*
X  Open a file for writing or appending. If 'test' is set, make sure
X  the file is writeable, notify the server and close the file; else prepare
X  for the transfer. When the server is inquiring about a file's write
X  permissions it sends zero bytes as the length of the message.
X
X  Returns: 0 on succes, -1 otherwise.
X*/
X
Xint open_file (char *file, int mode, int test)
X{
X  char msg [MAX_LINE];
X  int fd;
X
X  if ((fd = open (file, O_CREAT | mode, 0600)) < 0)
X    to_file = 0,
X    sprintf (msg, "%s: Permission denied.\n", file),
X    write_to_fd (ofd, msg, strlen (msg)),
X    nbytes = prompt_len,
X    sprintf (msg, "%d", PERMISSION_DENIED);
X  else {
X    if (nbytes > 0)
X      to_file = 1;
X    else
X      sprintf (msg, "%d", OK);
X    close (fd);
X  }
X  if (test)
X    if (write_to_fd (sock_fd, msg, strlen (msg)) < 0)
X      return -1;
X  return 0;
X}
X
X/*
X  Identify the server's response and take appropriate action.
X
X  Returns: 0 on success, -1 otherwise.
X*/
X
Xint check_server_response (int cmd, char *file, int response_timeout)
X{
X    switch (cmd) {
X    case OK:
X    case CONNECT:
X    case SYNTAX_ERROR:
X    case INVALID_REQ:
X    case BAD_ARCHIVE:
X    case RESTRICTED_REQ:
X    case NOT_OWNER:
X    case SYS_ERROR:
X    case SERVER_BUSY:
X    case PERMISSION_DENIED:
X    case CONN_CLOSED:
X    case PEER_UNAVAIL:
X    case MORE_INPUT_REQUIRED:
X    case CONTINUED: break;
X    case PASSWORD_REQUIRED:
X	 if (read_from_fd (sock_fd, nbytes, response_timeout) < 0) return -1;
X	 break;
X    case CONN_ABORTED:
X    case CONN_TIMEOUT: return -1; break;
X    case TEST_FILE_PERMISSIONS:
X	 if (open_file (file, O_WRONLY | O_APPEND, 1)) return -1;
X	 break;
X    case WRITE_TO_FILE_ASC:
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case WRITE_TO_FILE_BIN:
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_ASC:
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_BIN:
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    default: write_to_fd (ofd, "Protocol error.\n", 16); return -1;
X  }
X  return 0;
X}
X
X/*
X  Check for input redirection, and if so check for for permissions too.
X
X  Returns: the opened file descriptor on succes, -1 on error.
X*/
X
Xint check_for_redirection (char *buf, char *infile)
X{
X  char *redirect, *s;
X  int fd = 0;
X
X  if ((redirect = strrchr (buf, '<')) != NULL)
X    if (redirect != buf && *(redirect - 1) == '\\')
X      sprintf (redirect - 1, "%s", redirect); /* Remove \ */
X    else {
X      RESET (infile);
X      sscanf (redirect + 1, "%s", infile);
X      if (infile [0] == EOS) {
X	write_to_fd (ofd, "Invalid null input redirect.\n", 29);
X	return -1;
X      }
X      if ((fd = open (infile, O_RDONLY)) < 0) {
X	write_to_fd (ofd, "File not found or inadequate permissions.\n", 42);
X	return -1;
X      }
X      s = redirect + 1; /* Remove < and file name */
X      while (*s == ' ' || *s == '\t') ++s;
X      while (*s != ' ' && *s != '\t' && *s != '\n') ++s;
X      sprintf (redirect, "%s", s);
X    }
X  return fd;
X}
X#endif
*-*-END-of-src/siul.c-*-*
echo x - src/start.c
sed 's/^X//' >src/start.c <<'*-*-END-of-src/start.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                  UNIX LISTSERVER SYSTEM HOUSEKEEPER                      |
X  |                                                                          |
X  |                              Version 2.8                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas		     |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  This is the proper way of starting the system. The program verifies
X  that no other serverd, queued, list or listserv programs are running on the 
X  system (and kills them before proceeding -- after confirming), 
X  makes sure the required files exist (and creates new ones if missing -- 
X  after confirming), backs up all reports into files with extension .acc,
X  creates new directories for new mailing lists as necessary,
X  and finally spawns serverd. start reports to REPORT_START.
X
X  COMMAND LINE OPTIONS:
X    -k: just kill all pertinent programs that may already be running; no
X	mailing list is started in this case.
X    -c: Do not confirm before killing a process.
X    -r: Do not report; useful when starting up when system is rebooted.
X    -s: After a process is killed, start sleeps for some time. This turns
X	sleeping off -- to be used only when restarting the system via
X	a 'restart' request.
X  
X  WARNING: The program won't work correctly in the absence of an extended
X  egrep facility that does matching at the end of a line with a $ and 
X  accepts multiple regular expressions separated by a |. In this case,
X  the user may have to manually look for and terminate any such programs.
X  Also when the output of the 'ps' command exceeds 80 characters (due perhaps
X  to long path names) the user may again have to terminate programs by hand.
X  Note that a file locking mechanism  for serverd, list and listserv is used 
X  to ensure against multiple executions of the same program.
X
X  WARNING: When using the SYSV ps command and it chops output to 80 characters,
X  start may not be able to locate processes already running; this may occur if
X  the path to HOMEDIR is too long.
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <string.h>
X#ifdef SYSLOG
X# ifdef ultrix
X#  include <sys/syslog.h>
X# else
X#  include <syslog.h>
X# endif
X#endif
X#include <signal.h>
X#if !defined (__convex__) && !defined (__NeXT__) && !defined (apollo) \
X && !defined (sequent) && !defined (unknown_port)
X# include <malloc.h>
X#endif
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/stat.h>
X#include "defs.h"
X#include "start.h"
X#include "struct.h"
X#include "global.h"
X#if defined (__NeXT__) || defined (unknown_port)
X# include "next.h"
X#endif
X
X/*
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X# include "ansi/misc.h"
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include "nonansi/misc.h"
X# include <varargs.h>
Xextern int  syscom ();
Xextern char *tsprintf ();
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern char *extract_filename (char *);
Xextern void report_progress (FILE *, char *, int);
Xextern int  _getopt (int, char **, char *);
Xextern void setup_string (char *, char *, char *);
Xextern void shrink (char *);
Xextern char *mystrdup (char *);
Xextern int echo (char *, char *);
Xextern int echo_append (char *, char *);
Xextern int mv (char *, char *);
Xextern int cp (char *, char *);
Xextern int cat (char *, char *);
Xextern int cat_append (char *, char *);
Xextern int touch (char *);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern int otoi (char *);
X
Xvoid   main (int, char **);
XBOOLEAN check_for (char *, FILE *, BOOLEAN, BOOLEAN);
Xvoid   check_unprocessed (char *, FILE *, BOOLEAN);
Xvoid   usage (void);
Xvoid   backup (char *, char *);
Xvoid   start_config (char *);
Xint    gexit (int);
Xchar   get_response (void);
X
Xvoid main (int argc, char **argv)
X{
X  char command [256];
X  char line [MAX_LINE];
X  char dir [MAX_LINE];
X  char *addr1, *addr2, *addr3, *addr4, *options = "ckrs";
X  int c;
X  FILE *f, *p;
X  int i, nlists, pid, procs_killed = 0, procs;
X  BOOLEAN just_kill = FALSE, confirm = TRUE, do_report = TRUE, sleepok = TRUE;
X  char *tmpps, *tmpfound, *tmpnprocs;
X  struct stat stat_buf;
X  extern char *getenv();
X  
X  while ((c = _getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case 'c': confirm = FALSE; break;
X      case 'k': just_kill = TRUE; break;
X      case 'r': do_report = FALSE; break;
X      case 's': sleepok = FALSE; tty_echo = FALSE; break;
X      case '?':
X      default:
X	usage();
X    }
X
X  /* First make sure no other SERVERD programs are running. If so, kill
X     them all (after confirming) before proceeding. */
X 
X  init_signals ();
X  catch_signals ();
X  signal (SIGINT, (void (*)()) gexit);
X  signal (SIGALRM, SIG_IGN);
X  if ((mask = getenv ("ULISTSERVER_UMASK")))
X    umask (otoi (mask));
X  else
X    mask = "066",
X    umask (S_IRWXG|S_IRWXO); /* 600 */
X#ifdef SYSLOG
X  openlog ("UNIX ListServer: catmail", LOG_NDELAY
X# ifndef i386
X	   |LOG_NOWAIT
X# endif
X	   , SYSLOG);
X# ifndef ultrix
X  setlogmask (LOG_UPTO (LOG_INFO));
X# endif
X#else
X  backup (REPORT_START, REPORT_START_ACC);
X  if ((report = fopen (REPORT_START, "a")) == NULL)
X    fprintf (stderr, "start: Could not open %s\n", REPORT_START),
X    START_ABORT;
X  chmod (REPORT_START, 384); /* 600 */
X#endif
X  nlists = sys_config (report, &sys);
X  if (just_kill)
X    report_progress (report, "\n--- SHUTTING UNIX LISTSERVER SYSTEM DOWN ---\n",
X		     FALSE);
X  else
X    report_progress (report, "\n--- STARTING UNIX LISTSERVER SYSTEM ---\n",
X		     FALSE);
X#ifndef _MINIX
X  if (sys.options & BSD_PS)
X    syscom ("ps -gx > %s", (tmpps = mystrdup (tmpnam (NULL))));
X  else
X    syscom ("ps -ef | grep %s > %s",
X	    ((addr1 = (char *) getenv ("LOGNAME")) == NULL ?
X	      ((addr1 = (char *) getenv ("USER")) == NULL ? "server" : addr1) :
X	      addr1), (tmpps = mystrdup (tmpnam (NULL))));
X  tty_echo = FALSE;
X  addr1 = extract_filename (SERVERD);
X  addr2 = extract_filename (LIST);
X  addr3 = extract_filename (SERVER);
X  addr4 = extract_filename (PQUEUE);
X  syscom ("egrep '%s$|%s|%s$|%s |%s$|%s |%s|%s |queued' %s > %s",
X	  addr1, addr1, addr2, addr2, addr3, addr3, addr4, addr4, tmpps,
X	  (tmpfound = mystrdup (tmpnam (NULL))));
X  free ((char *) addr1);
X  free ((char *) addr2);
X  free ((char *) addr3);
X  free ((char *) addr4);
X  unlink (tmpps);
X  free ((char *) tmpps);
X  if (sleepok)
X    tty_echo = TRUE;
X  if ((f = fopen (tmpfound, "r")) == NULL)
X    report_progress (report,
X		     tsprintf ("Error opening %s. Aborting. %s", tmpfound,
X			       ((just_kill == FALSE) ?
X				"System not started." : "")), TRUE),
X    START_ABORT;
X  
X  syscom ("wc -l %s > %s", tmpfound, (tmpnprocs = mystrdup (tmpnam (NULL))));
X  if ((p = fopen (tmpnprocs, "r")) != NULL) {
X    fscanf (p, "%d", &procs);
X    if (do_report)
X      report_progress (report,
X		       tsprintf ("OLD UNIX LISTSERVER PROCESSES RUNNING: %d\n",
X				 procs), FALSE);
X    fclose (p);
X    unlink (tmpnprocs);
X    free ((char *) tmpnprocs);
X  }
X  else
X    report_progress (report,
X		     tsprintf ("Error opening %s. Aborting. %s", tmpnprocs,
X			       ((just_kill == FALSE) ?
X				"System not started." : "")), TRUE),
X    START_ABORT;
X  
X  while (!feof (f)) {  /* get pid's and kill processes after confirming */
X    RESET (line);
X    fgets (line, MAX_LINE - 2, f);
X    if (line[0] != EOS) {
X      if (sys.options & BSD_PS)
X        sscanf (line, "%d ", &pid);
X      else
X        sscanf (line, "%s %d ", command, &pid);
X      if (confirm) {
X# ifdef SIGSTOP
X	kill (pid, SIGSTOP);
X# elif defined (SIGSTP)
X	kill (pid, SIGSTP);
X# endif
X	c = EOS;
X        while (c != EOF && c != 'Y' && c != 'N') {
X# ifdef SYSLOG
X	  printf ("\n%s\n%c[7m%s%c[mKill it to proceed ? (capital Y/N) ",
X		  ((just_kill == FALSE) ? 
X		   "ERROR: another UNIX ListServer system program running:" : 
X		   "UNIX ListServer system program found:"),
X		  27, line, 27);
X# else
X	  report_progress (report,
X			   tsprintf ("\n%s\n%c[7m%s%c[mKill it to \
Xproceed ? (capital Y/N) ",
X				     ((just_kill == FALSE) ? 
X				      "ERROR: another UNIX ListServer system program running:" : 
X				      "UNIX ListServer system program found:"),
X				     27, line, 27), FALSE);
X# endif
X	  c = get_response ();
X        }
X# ifdef SIGCONT
X	kill (pid, SIGCONT);
X# elif defined (SIGCNT)
X	kill (pid, SIGCNT);
X# endif
X        if (c == 'N' || c == EOF)
X	  report_progress (report, tsprintf ("start aborted. %s\n",
X					     ((just_kill == FALSE) ?
X					      "System not started.":"")), TRUE),
X	  unlink (tmpfound),
X	  exit (1);
X      }
X      kill (pid, SIGINT);
X      ++procs_killed;
X    }
X  }
X  fclose (f);
X  unlink (tmpfound);
X  free ((char *) tmpfound);
X  if (sleepok)
X    sleep (2);
X  for (i = 0; pids[i]; i++)
X    if ((f = fopen (pids[i], "r")) != NULL)
X      fscanf (f, "%d", &pid),
X      kill (pid, SIGINT),
X      ++procs_killed,
X      fclose (f),
X      unlink (pids[i]);
X  if (do_report)
X    report_progress (report, tsprintf ("OLD UNIX LISTSERVER PROCESSES KILLED: \
X%d\n", procs_killed), FALSE);
X  
X  if (just_kill)  /* Done */
X    report_progress (report, "", -TRUE),
X# ifdef SYSLOG
X    closelog (),
X# else
X    fclose (report),
X# endif
X    exit (0);
X  if (sleepok)
X    sleep (2);
X#endif
X  
X  echo ("Serverd lock file", SERVERD_LOCK_FILE);
X  echo ("List lock file", LIST_LOCK_FILE);
X  echo ("Server lock file", SERVER_LOCK_FILE);
X  echo ("Pqueue lock file", PQUEUE_LOCK_FILE);
X
X  backup (REPORT_SERVER, REPORT_SERVER_ACC);
X  backup (REPORT_SERVERD, REPORT_SERVERD_ACC);
X  backup (REPORT_PQUEUE, REPORT_PQUEUE_ACC);
X  SETUP_IGNOREDF;
X  check_for (server_ignoredf, report, do_report, confirm);
X  check_for (OWNERSF, report, do_report, confirm);
X  check_for (ALIASESF, report, do_report, confirm);
X  shrink (REPORT_CATMAIL);
X  syscom ("chmod u+s %s", CATMAIL);
X  for (i = 0; i < nlists; ++i) {
X    start_config (sys.lists[i].alias);
X    SETUP_DIR;
X    if (stat (dir, &stat_buf)) {
X      if (mkdir (dir, /*448*/ 0777 & (0777 ^ otoi (mask))))
X	report_progress (report, tsprintf ("Could not create directory %s", dir),
X			 TRUE),
X	START_ABORT;
X      if (do_report)
X        report_progress (report, tsprintf ("*** New list %s ***\n",
X					   sys.lists[i].alias), FALSE);
X      cp (server_ignoredf, dir);
X      syscom ("echo '^%s$' >> %s/%s", sys.lists[i].alias, dir, IGNORED);
X      echo_append (sys.lists[i].address, tsprintf ("%s/%s", dir, IGNORED));
X      syscom ("echo %s | sed 's/listserver/server/' | sed 's/listserv/server/' \
X>> %s/%s", sys.server.address, dir, IGNORED);
X      touch (infof);
X      touch (welcomef);
X      touch (list_mail_f);
X      chmod (list_mail_f, /*416*/ 0666 & (0666 ^ otoi (mask)));
X      touch (tsprintf ("%s/%s", dir, MODERATED_MAIL_FILE));
X      chmod (tsprintf ("%s/%s", dir, MODERATED_MAIL_FILE),
X	     0666 & (0666 ^ otoi (mask)));
X    }
X    check_for (subscribersf, report, do_report, confirm);
X    if (!check_for (aliasesf, report, do_report, confirm))
X      echo_append (DEFAULT_ALIASES, aliasesf);
X    check_for (newsf, report, do_report, confirm);
X    check_for (peersf, report, do_report, confirm);
X    check_for (restrictedf, report, do_report, confirm);
X    check_unprocessed (sys.lists[i].alias, report, do_report);
X    backup (report_listf, report_list_accf);
X  }
X  syscom ("%s %s &", SERVERD, sys.serverd_cmdoptions);
X  report_progress (report, "", -TRUE);
X#ifdef SYSLOG
X  closelog ();
X#else
X  fclose (report);
X#endif
X  exit (0);
X}
X
X/*
X  Make sure that file 's' exists. Create a new one if necessary. Return
X  TRUE if file exists, FALSE otherwise.
X*/
X
XBOOLEAN check_for (char *s, FILE *report, BOOLEAN do_report, BOOLEAN confirm)
X{
X  int c = EOS;
X  struct stat stat_buf;
X
X  if (stat (s, &stat_buf)) { /* make sure we have file 's' */
X    if (confirm) {
X      while (c != EOF && c != 'Y' && c != 'N') {
X 	if (do_report)
X#ifdef SYSLOG
X          printf ("No %s file found. Create a new one? (capital Y/N) ",s),
X#else
X	  report_progress (report, tsprintf ("No %s file found. Create a new \
Xone? (capital Y/N) ",s), FALSE);
X#endif
X        c = get_response ();
X      }
X      if (c == 'N' || c == EOF)
X        START_ABORT;
X    }
X    touch (s);
X    return FALSE;
X  }
X  return TRUE;
X}
X
X/*
X  Check for incomplete delivery for a list during the last run. The files
X  .unprocessed.* in the list's directory signify undelivered mail for
X  a subset of the subscribers/newsgroups/peers. Prepend the last undelivered
X  messages to the mail file, so that they are distributed again to the remaining
X  subscribers/newsgroups/peers.
X*/
X
Xvoid check_unprocessed (char *list, FILE *report, BOOLEAN do_report)
X{
X  struct stat stat_buf;
X  char *tmpmail;
X
X  if (!stat (unprocessed_subscribersf, &stat_buf) ||
X      !stat (unprocessed_peersf, &stat_buf) ||
X      !stat (unprocessed_newsf, &stat_buf) ||
X      !stat (unprocessed_digestf, &stat_buf) ||
X      !stat (unprocessed_messages, &stat_buf)) { /* Last delivery incomplete */
X    if (do_report)
X      report_progress (report, tsprintf ("\nList %s did not complete last \
Xdelivery\n", list), FALSE);
X    if (stat (unprocessed_messages, &stat_buf) &&
X	stat (unprocessed_digestf, &stat_buf)) {
X	if (do_report)
X	  report_progress (report, tsprintf ("Last set of mail messages for \
Xlist %s lost; cannot resume delivery\n", list), FALSE);
X        unlink (unprocessed_subscribersf);
X        unlink (unprocessed_peersf);
X        unlink (unprocessed_newsf);
X        return;
X      }
X      else if (do_report)
X        report_progress (report, "Mail will be delivered to the remaining \
Xsubscribers/newsgroups/peers shortly\n", FALSE);
X    /* Create empty .unprocessed files for subscribers/newsgroups/peers
X       that do not exist. */
X    touch (unprocessed_subscribersf);
X    touch (unprocessed_peersf);
X    touch (unprocessed_newsf);
X    /* Now prepend undelivered message(s) to the mail file */
X    mv (list_mail_f, (tmpmail = mystrdup (tmpnam (NULL))));
X    cat (unprocessed_messages, list_mail_f);
X    cat_append (tmpmail, list_mail_f);
X    unlink (tmpmail);
X    free ((char *) tmpmail);
X    unlink (unprocessed_messages);
X    chmod (list_mail_f, /*416*/ 0666 & (0666 ^ otoi (mask)));
X  }
X}
X
X/*
X  Append 'src' to 'dest' and create a brand new 'src'.
X*/
X
Xvoid backup (char *src, char *dest)
X{
X  struct stat stat_buf;
X
X  if (!stat (src, &stat_buf)) /* prepare for backup */
X    cat_append (src, dest),
X    chmod (dest, 384), /* 600 */
X    unlink (src),
X    touch (src),
X    chmod (src, 384);
X}
X
X/*
X  Print usage.
X*/
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: start [-k] [-c] [-r]\n\
X-k: Just kill any UNIX ListServer system programs running.\n\
X-c: Do not confirm before killing processes.\n\
X-r: Restrict reporting to screen.\n");
X  exit (1);
X}
X
Xvoid start_config (char *alias)
X{
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (aliasesf, alias, ALIASES);
X  setup_string (newsf, alias, NEWSF);
X  setup_string (peersf, alias, PEERS);
X  setup_string (restrictedf, alias, RESTRICTED);
X  setup_string (ignoredf, alias, IGNORED);
X  setup_string (report_listf, alias, REPORT_LIST);
X  setup_string (infof, alias, INFO_FILE);
X  setup_string (welcomef, alias, WELCOME_FILE);
X  setup_string (unprocessed_subscribersf, alias, UNPROC_SUBSCRIBERS);
X  setup_string (unprocessed_peersf, alias, UNPROC_PEERS);
X  setup_string (unprocessed_newsf, alias, UNPROC_NEWS);
X  setup_string (unprocessed_messages, alias, UNPROC_MESSAGES);
X  setup_string (unprocessed_digestf, alias, UNPROC_DIGEST);
X  setup_string (list_mail_f, alias, LIST_MAIL_FILE);
X  setup_string (report_list_accf, alias, REPORT_LIST_ACC);
X}
X
X/*
X  Required to avoid undefined symbols.
X*/
X
Xint gexit (int exitcode)
X{
X  exit (exitcode);
X}
X
X/*
X  Get a one-character response from stdin.
X*/
X
Xchar get_response ()
X{
X  char c;
X
X  fflush (stdout);
X  fflush (stdin);
X  c = fgetc (stdin);
X
X  if (c != '\n' && c != EOF)
X    while (fgetc (stdin) != '\n');
X  fflush (stdin);
X  return c;
X}
*-*-END-of-src/start.c-*-*
echo x - src/strftime.c
sed 's/^X//' >src/strftime.c <<'*-*-END-of-src/strftime.c-*-*'
X#ifndef lint
X#ifndef NOID
Xstatic char	elsieid[] = "@(#)strftime.c	7.1";
X/*
X** Based on the UCB version whose ID appears below.
X** This is ANSIish only when time is treated identically in all locales and
X** when "multibyte character == plain character".
X*/
X#endif /* !defined NOID */
X#endif /* !defined lint */
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "@(#)strftime.c	5.4 (Berkeley) 3/14/89";
X#endif /* LIBC_SCCS and not lint */
X
X#include <sys/types.h>
X#include <time.h>
X#ifdef HAVE_TZFILE_H
X# include <tzfile.h>
X#else
X# define TM_YEAR_BASE    1900
X#endif
X
Xstatic char *afmt[] = {
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
X};
Xstatic char *Afmt[] = {
X	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
X	"Saturday",
X};
Xstatic char *bfmt[] = {
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
X	"Oct", "Nov", "Dec",
X};
Xstatic char *Bfmt[] = {
X	"January", "February", "March", "April", "May", "June", "July",
X	"August", "September", "October", "November", "December",
X};
X
Xstatic size_t gsize;
Xstatic char *pt;
X
Xstatic void _add();
Xstatic void _conv();
Xstatic void _fmt();
X
Xsize_t
Xucb_strftime(s, maxsize, format, t)
X	char *s;
X	char *format;
X	size_t maxsize;
X	struct tm *t;
X{
X	pt = s;
X	gsize = maxsize;
X	_fmt(format, t);
X	if (gsize <= 0)
X		return(0);
X	*pt = '\0';
X	return(maxsize - gsize);
X}
X
Xstatic void
X_fmt(format, t)
X	register char *format;
X	struct tm *t;
X{
X	for (; *format; ++format) {
X		if (*format == '%')
X			switch(*++format) {
X			case '\0':
X				--format;
X				break;
X			case 'A':
X				if (t->tm_wday < 0 || t->tm_wday > 6)
X					_add("?");
X				else	_add(Afmt[t->tm_wday]);
X				continue;
X			case 'a':
X				if (t->tm_wday < 0 || t->tm_wday > 6)
X					_add("?");
X				else	_add(afmt[t->tm_wday]);
X				continue;
X			case 'B':
X				if (t->tm_mon < 0 || t->tm_mon > 11)
X					_add("?");
X				else	_add(Bfmt[t->tm_mon]);
X				continue;
X			case 'b':
X			case 'h':
X				if (t->tm_mon < 0 || t->tm_mon > 11)
X					_add("?");
X				else	_add(bfmt[t->tm_mon]);
X				continue;
X			case 'c':
X				_fmt("%D %X", t);
X				continue;
X			case 'C':
X				_fmt("%a %b %e %X %Y", t);
X				continue;
X			case 'D':
X			case 'x':
X				_fmt("%m/%d/%y", t);
X				continue;
X			case 'd':
X				_conv(t->tm_mday, 2, '0');
X				continue;
X			case 'e':
X				_conv(t->tm_mday, 2, ' ');
X				continue;
X			case 'H':
X				_conv(t->tm_hour, 2, '0');
X				continue;
X			case 'I':
X				_conv(t->tm_hour % 12 ?
X				    t->tm_hour % 12 : 12, 2, '0');
X				continue;
X			case 'j':
X				_conv(t->tm_yday + 1, 3, '0');
X				continue;
X			case 'k':
X				_conv(t->tm_hour % 12 ?
X				    t->tm_hour % 12 : 12, 2, ' ');
X				continue;
X#ifdef KITCHEN_SINK
X			case 'K':
X				_add("kitchen sink");
X				continue;
X#endif /* defined KITCHEN_SINK */
X			case 'l':
X				_conv(t->tm_hour, 2, ' ');
X				continue;
X			case 'M':
X				_conv(t->tm_min, 2, '0');
X				continue;
X			case 'm':
X				_conv(t->tm_mon + 1, 2, '0');
X				continue;
X			case 'n':
X				_add("\n");
X				continue;
X			case 'p':
X				_add(t->tm_hour >= 12 ? "PM" : "AM");
X				continue;
X			case 'R':
X				_fmt("%H:%M", t);
X				continue;
X			case 'r':
X				_fmt("%I:%M:%S %p", t);
X				continue;
X			case 'S':
X				_conv(t->tm_sec, 2, '0');
X				continue;
X			case 'T':
X			case 'X':
X				_fmt("%H:%M:%S", t);
X				continue;
X			case 't':
X				_add("\t");
X				continue;
X			case 'U':
X				_conv((t->tm_yday + 7 - t->tm_wday) / 7,
X					2, '0');
X				continue;
X			case 'W':
X				_conv((t->tm_yday + 7 -
X				    (t->tm_wday ? (t->tm_wday - 1) : 6))
X				    / 7, 2, '0');
X				continue;
X			case 'w':
X				_conv(t->tm_wday, 1, '0');
X				continue;
X			case 'y':
X				_conv((t->tm_year + TM_YEAR_BASE) % 100,
X					2, '0');
X				continue;
X			case 'Y':
X				_conv(t->tm_year + TM_YEAR_BASE, 4, '0');
X				continue;
X			case 'Z':
X#ifdef TM_ZONE
X				if (t->TM_ZONE)
X					_add(t->TM_ZONE);
X				else
X#endif /* defined TM_ZONE */
X				if (t->tm_isdst == 0 || t->tm_isdst == 1) {
X					extern char *	tzname[2];
X
X					_add(tzname[t->tm_isdst]);
X				} else	_add("?");
X				continue;
X			case '%':
X			/*
X			 * X311J/88-090 (4.12.3.5): if conversion char is
X			 * undefined, behavior is undefined.  Print out the
X			 * character itself as printf(3) also does.
X			 */
X			default:
X				break;
X		}
X		if (gsize <= 0)
X			return;
X		*pt++ = *format;
X		--gsize;
X	}
X}
X
Xstatic void
X_conv(n, digits, fill)
X	int n, digits, fill;
X{
X	static char buf[10];
X	register char *p;
X
X	for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
X		*p-- = n % 10 + '0';
X	while (p > buf && digits-- > 0)
X		*p-- = fill;
X	_add(++p);
X}
X
Xstatic void
X_add(str)
X	register char *str;
X{
X	for (;; ++pt, --gsize) {
X		if (gsize <= 0)
X			return;
X		if (!(*pt = *str++))
X			return;
X	}
X}
X
*-*-END-of-src/strftime.c-*-*
echo x - src/sysmail.c
sed 's/^X//' >src/sysmail.c <<'*-*-END-of-src/sysmail.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                       SYSTEM MAILING ROUTINES                            |
X  |									     |
X  |                             Version 2.6                                  |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  These routines implement the 'system' mailmethod. The system opens a socket
X  and connects directly with sendmail for mail delivery. During the process
X  of porting the code to various platforms, lots of grosse things were
X  encountered with sockets and protocols, so the code may seem kludgy.
X
X  When mail cannot be sent due to network problems, it is queued up and will 
X  be delivered later by the queue daemon.
X*/
X
X#include <stdio.h>
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "defs.h"
X#include "struct.h"
X#include "sysmail.h"
X#ifdef TCP_IP
X# include <fcntl.h>
X# include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X# include <sys/types.h>
X# include <sys/socket.h>
X# include <netinet/in.h>
X# include <netdb.h>
X#endif
X
X#ifdef __STDC__
X# include <stdarg.h>
Xextern int  syscom (char *, ...);
Xextern char *tsprintf (char *, ...);
X#else
X# include <varargs.h>
Xextern int  syscom ();
Xextern char *tsprintf ();
X#endif
Xextern FILE *report;
Xextern SYS sys;
Xextern BOOLEAN debug;
Xextern int listid;
X
X#ifndef __NeXT__
Xextern long int atoi (char *);
X#else
Xextern int atoi (const char *);
X#endif
Xextern void report_progress (FILE *, char *, int);
Xextern int gexit (int);
Xextern int echo (char *, char *);
Xextern int mv (char *, char *);
X
XBOOLEAN sysmail (char *);
X#ifdef TCP_IP
XBOOLEAN _sysmail (char *, int);
Xint     server_response (int);
Xint     build_tcp_connection (FILE *);
Xvoid    queue_up (char *file);
X#endif
X
X/*
X  The return value depends on the return value of _sysmail ().
X*/
X
XBOOLEAN sysmail (char *file)
X{
X#ifdef TCP_IP
X  queue = FALSE;
X  if (debug) {
X    OPEN_FILE (sent, SENT, "w", "sysmail");
X    OPEN_FILE (received, RECEIVED_, "w", "sysmail");
X  }
X  return _sysmail (file, 1);
X#endif
X}
X#ifdef TCP_IP
X
X/*
X  'system' mailmethod. It returns TRUE if the mail was successfully
X  delivered, FALSE if it was queued.
X*/
X
XBOOLEAN _sysmail (char *file, int call)
X{
X  char buf [MAX_LINE] ;
X  FILE *msg;
X  int sock_fd, cmd, bytes_to_write, bytes_written, i;
X   
X  OPEN_FILE (msg, file, "r", "_sysmail");
X  if ((sock_fd = build_tcp_connection (report)) < 0) {
X    queue = TRUE;
X    goto abort;
X  }
X  if ((cmd = server_response (sock_fd)) != ACKNOWLEDGE) {
X    report_progress (report, "\n_sysmail(): Could not connect to sendmail",
X		     TRUE);
X    queue = TRUE;
X    goto abort;
X  }
X
X  PROTOCOL (DATA);
X  while (!feof (msg) && strcmp (buf, END_OF_TEXT)) { /* Copy header and text */
X    RESET (buf);
X    fgets (buf, MAX_LINE - 2, msg);
X    WRITE_TO_SOCKET (sock_fd, buf);
X  }
X  cmd = server_response (sock_fd);
X  CHECK_SERVER_RESPONSE (cmd, OK, NULL);
X  PROTOCOL (CLOSE_CONNECTION);
X
X  abort:
X  if (queue)
X    queue_up (file);
X  CLOSEF;
X  if (debug)
X    fclose (sent),
X    fclose (received);
X  return !queue;
X}
X
X/*
X  Get server response. Return the command number that sendmail sent,
X  CONN_TIMEOUT or HOST_ABORTED.
X*/
X
Xint server_response (int sock_fd)
X{
X  char buf [MAX_LINE];
X  long int cmd, i, column = 3, bytes_to_read = 3, bytes_read = 0, time_started;
X  BOOLEAN done = FALSE, continued = FALSE;
X
X  time_started = time (0);
X  RESET (buf);
X  while (bytes_to_read) {
X    errno = 0;
X    /* Get server command */
X    bytes_read = read (sock_fd, &buf [3 - bytes_to_read], bytes_to_read);
X    if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	errno == ECONNREFUSED)
X      return HOST_ABORTED;
X    if (errno || bytes_read < 1)
X      if ((time (0) - time_started) > TIMEOUT)
X	return CONN_TIMEOUT;
X    if (bytes_read > 0)
X      bytes_to_read -= bytes_read;
X  }
X  RESET (message);
X  buf[3] = EOS;
X  cmd = atoi (buf);
X  sprintf (message, "%s", buf);
X  i = strlen (message);
X  if (debug)
X    fprintf (received, "%s", buf),
X    fflush (received);
X  while (!done) { /* Read till the end of socket */
X    errno = 0;
X    while ((bytes_read = read (sock_fd, buf, 1)) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	  errno == ECONNREFUSED)
X	return HOST_ABORTED;
X      if (errno)
X	if ((time (0) - time_started) > TIMEOUT)
X	  return CONN_TIMEOUT;
X    }
X    ++column;
X    if (buf[0] == '\n') {
X      column = 0;
X      if (!continued)
X	done = TRUE;
X    }
X    if (column == 4)
X      if (buf[0] != '-')
X	continued = FALSE;
X      else
X	continued = TRUE;
X    message [i++] = (buf[0] != '\n' ? buf[0] : EOS);
X    if (debug)
X      fprintf (received, "%c", buf[0]),
X      fflush (received);
X  }
X  return cmd;
X}
X
X/*
X  Establish connection with sendmail/smtp.
X*/
X
Xint build_tcp_connection (FILE *report)
X{
X  int sock_fd;
X  struct sockaddr_in sin;
X  struct hostent *hostentry;
X   
X  if (! (hostentry = gethostbyname (SENDMAIL_HOST))) {
X    report_progress (report, "\nbuild_tcp_connection(): No such host", TRUE);
X    return -1;
X  }
X  if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X    report_progress (report, "\nbuild_tcp_connection(): Could not create \
Xsocket", TRUE);
X    return -1;
X  }
X  sin.sin_family = AF_INET;
X  sin.sin_port = htons (PORT);
X  memcpy ((char *) &sin.sin_addr.s_addr, (char *) hostentry->h_addr,
X	  hostentry->h_length);
X  memset (sin.sin_zero, EOS, sizeof (sin.sin_zero));
X  if (connect (sock_fd, (struct sockaddr *) &sin, sizeof (struct sockaddr_in))
X      < 0) {
X    report_progress (report, "\nbuild_tcp_connection(): Could not connect to \
Xport", TRUE);
X    close (sock_fd);
X    return -1;
X  }
X# ifdef NONBLOCKING_IO
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | O_NDELAY)) < 0)
X    report_progress (report, "\nbuild_tcp_connection(): WARNING: timeout \
Xmechanism not functional", TRUE);
X# endif
X  return sock_fd;
X}
X
X/*
X  Queue up a file for later delivery.
X*/
X
Xvoid queue_up (char *file)
X{
X  FILE *id;
X  int id_no = 0;
X  char msg [MAX_LINE];
X
X  if ((id = fopen (IDF, "r")) != NULL)
X    fscanf (id, "%d\n", &id_no),
X    fclose (id);
X  mv (file, tsprintf ("%s/%d", QUEUE_DIR, ++id_no));
X  echo (tsprintf ("%d", id_no), IDF);
X  sprintf (msg, "File %s placed in the mail queue", file);
X  report_progress (report, msg, TRUE);
X}
X#endif
*-*-END-of-src/sysmail.c-*-*
echo x - src/tlock.c
sed 's/^X//' >src/tlock.c <<'*-*-END-of-src/tlock.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                		tlock UTILITY 				     |
X  |                                                                          |
X  |                              Version 2.0                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely only as a    |
X  | whole and not in parts, as long as you do not remove or alter the author |
X  | and copyright notices in the file defs.h; this notices are #define'd in  |
X  | the symbols VERSION and COPYRIGHT. Although you may alter the code	     |
X  | provided for your personal use, you you may not alter the functions      |
X  | create_header(), create_multi_recipient_header() and main() in list.c,   |
X  | listserv.c and serverd.c (where applicable), and you may not redistribute|
X  | any changes you may have made. No part of the source code bearing a	     |
X  | copyright notice can be included in commercial software systems without  |
X  | written permission by the author.					     |
X  | By using this software you are bound by this agreement.                  |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas               |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  |									     |
X  | Enhanced by: Warren Burnstein.					     |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <fcntl.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include "defs.h"
X#include "struct.h"             /* global.h needs it */
X#include "global.h"             /* misc.c needs it */
X
XBOOLEAN tty_echo = TRUE;        /* ditto */
XFILE    *report  = NULL;        /* ditto */
X
Xextern void setup_string (char *, char *, char *);
Xextern char *locase (char *);
Xextern char *upcase (char *);
X
Xvoid main ();
Xint  check (char *);
Xint  gexit (int);
X
Xvoid main ();
X
Xchar *s[] = { 
X  SERVERD_LOCK_FILE, LIST_LOCK_FILE, SERVER_LOCK_FILE, PQUEUE_LOCK_FILE,
X  SERVER_MAIL_FILE, BATCH_FILE, NULL
X};
X
X/*
X  Test whether any locks exist on the above files plus the mail and moderated
X  files of each list by other processes.
X
X  USER CONTRIBUTED MODIFIED CODE: Warren Burnstein.
X*/
X
Xvoid main ()
X{
X#ifndef NO_LOCKS
X  char cmd[MAX_LINE], args[MAX_LINE], file[MAX_LINE], alias[MAX_LINE];
X  int i, locks = 0;
X  FILE *fp;
X
X  for (i = 0; s[i]; i++)
X    locks += check (s[i]);
X
X  if ((fp = fopen (CONFIG, "r")) == NULL) {
X    fprintf (stderr, "Cannot open %s\n", CONFIG);
X    gexit (1);
X  }
X
X  while (! feof (fp)) {
X    args [0] = RESET (cmd);
X    fscanf (fp, "%s", cmd);
X    if (cmd[0] == EOS)
X      continue;
X    fgets (args, MAX_LINE - 2, fp);
X    if (args [0] != EOS && args [strlen (args) - 1] == '\n')
X      args [strlen (args) - 1] = EOS;
X
X    if (strcmp (locase (cmd), "list") == 0 && sscanf (args, "%s", alias) == 1)
X      upcase (alias),
X      setup_string (file, alias, LIST_MAIL_FILE),
X      locks += check (file),
X      setup_string (file, alias, LIST_MODERATED_F),
X      locks += check (file);
X  }
X  fclose(fp);
X
X  if (!locks)
X    printf ("No files locked.\n"),
X    gexit (0);
X  gexit (locks + 1);
X#else
X  printf ("File locking mechanism not functional. This mechanism is turned off \
Xusually when\nfile locking is not supported over NFS. If this system is \
Xinstalled on a local\nfile system, you may consider undefining the symbol \
XNO_LOCKS in src/defs.h;\ncheck also with your system manuals to verify that \
Xfile locking over NFS is\nindeed not supported.\n");
X  gexit (-1);
X#endif
X}
X
X/*
X  Check whether a file is locked.
X
X  USER CONTRIBUTED FUNCTION: Warren Burnstein.
X*/
X
Xint check (char *s)
X{
X#ifndef NO_LOCKS
X  int fd, ret = 0;
X
X  if (lockf ((fd = open (s, O_RDWR)), F_TLOCK, 0))
X    if (errno != EBADF)
X      ret = 1,
X      printf ("Lock placed on %s\n", s);
X  close (fd);
X  return ret;
X#endif
X}
X
X/*
X  Required to avoid undefined symbols.
X*/
X
Xint gexit (int exitcode)
X{
X  exit (exitcode);
X}
*-*-END-of-src/tlock.c-*-*
echo x - redux
sed 's/^X//' >redux <<'*-*-END-of-redux-*-*'
X#!/bin/sh
X# Script to reduce the size of mboxes by removing unnecessary fields in
X# the header of each message.
X# Usage: redux [mbox-file] (default is ./mbox)
X
XFILE=$1
Xif [ $# = 0 ]; then
X  FILE=./mbox
Xfi
Xif [ ! -r $FILE ]; then
X  echo File $FILE not found.
X  exit
Xfi
Xecho Reducing $FILE\; reduced file in /tmp/reduced >&2
X/usr/bin/egrep -v "^Cc: |^          with BSMTP;|^Received: |^Status: |^Message-Id: |^Comment: |^Reply-To: |^Sender: |^To: |^Return-Path: |^	id " $FILE > /tmp/reduced
Xexit
*-*-END-of-redux-*-*
echo x - flocks
sed 's/^X//' >flocks <<'*-*-END-of-flocks-*-*'
X#include "/usr/server/src/defs.h"
Xrm -f SERVERD_LOCK_FILE
Xrm -f LIST_LOCK_FILE
Xrm -f SERVER_LOCK_FILE
Xrm -f PQUEUE_LOCK_FILE
*-*-END-of-flocks-*-*
echo x - ulock
sed 's/^X//' >ulock <<'*-*-END-of-ulock-*-*'
X#!/bin/sh
X# ulock - in conjunction with the file 'flocks', this utility removes all
X#        locked files.
X/lib/cpp flocks | /bin/sh
*-*-END-of-ulock-*-*
echo x - config
sed 's/^X//' >config <<'*-*-END-of-config-*-*'
X
X#		UNIX ListServer system configuration file
X#
X#			       version 6.0
X#
X#			        June 1993
X#
X# AGREEMENT: This software can be used and distributed freely as long
X# as you do not remove or alter the Copyright notice in the file defs.h;
X# this notice is #define'd in the symbol VERSION. Although you may alter
X# the code provided, you may not alter the functions create_header()
X# and create_multi_recipient_header() in list.c and listserv.c.
X# By using this software you are bound by this agreement.
X# This software comes with no warranties and cannot be sold for profit.
X# The AGREEMENT and COPYRIGHT notices should be included in all source
X# files when distributing this software.
X# COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X# Use, duplication or disclosure by the Federal Government is subject to
X# the restrictions set forth in FAR 52.227-19(c), Commercial Computer
X# Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1)
X# (ii).
X#
X# Note: On SysV and BSD Unix systems 'sendmail' is located under /usr/lib/
X# but on SysVR4 it is located under /usr/ucblib/; also, check the path to
X# rmail if you are going to use it.
X
Xorganization Boston University	     # define your site
Xlist venus venus@host onwer@domain venus1 -e -m 25
X		 # define a list called 'venus' (echo to screen)
Xserver listserver@domain -e -r statistics -b get -a VENUS # define the server
X		 # with restriction, owner approved subscriptions for
X		 # list VENUS, and batch all 'get' requests
Xlist ermis ermis@domain owner@other-domain ermis1 -m 25 -e
X		 # define a list called 'ermis'
Xlist herc herc@domain owner@domain herc1 -r -s -m 25
X		 # define 'herc' w/ restricted mail and no subscription checking
Xheader ermis {	 # define header lines to be preserved
X  X-Mailer:	 # save the mailer string
X  X-Organization:
X  MIME-Version:
X}
Xarchive ermis /ftp/mailing-lists/ermis %y%m%d listserver/ermis &
Xjohny-be-good	 # auto archive list ermis -- command spans two lines
Xarchive venus /ftp/mailing-lists/venus venus.%j listserver/venus - digest
Xarchive herc  /usr/server/archives/private/herc %v.%n listserver/herc
Xdefault ermis {	 # define default mail mode and password for new subscribers
X mail = ack	 # if not defined, the system default is NOACK
X address = fixed # other option is variable; system default is FIXED
X password = xxx  # if not defined, the system assigns a random password
X conceal = yes   # system default is NO
X}
Xdefault venus {  # define default mail mode for new subscribers; the system
X  mail = digest	 # picks a password at random, does not conceal identities
X		 # and the user may not reset his address
X}
Xceiling ermis 10 # limit the number of messages processed per day to 10
Xdigest ermis 5000 48
X                 # specify the max number of lines before a digest is forced out
X                 # followed by the frequency (hours) that digests are
X                 # distributed
Xdigest venus 1000 24
X		 # these are the default settings
Xdigest herc 2000 72
X		 # digests will go out every 3 days, or when they grow
X		 # longer than 2000 lines
Xdisable herc statistics	 # cannot get statistics for list herc
Xunix_cmd ermis pass1 dir '/bin/ls $*' #Syntax: dir [args]
X		 # $* substitutes for all arguments
Xunix_cmd ermis pass1 swap '/bin/echo $2 $1' #Syntax: swap <arg1> <arg2>
X		 # re-echo the two arguments in the opposite order
Xbatch 8 20	 # batch requests between 8 am and 8 pm
Xfax /usr/bin/fax # Path to, and optional arguments to auto-faxing program
Xserverd -l 3 -e	-i 600
X		 # the serverd program may be run with the -l option
X		 # serverd will delay if the load average is above 3
X		 # Note that the -1 flag is used by default by start
X		 # Go into interactive mode also with a 10 minute duration
Xrestriction 20	 # certain requests may not be serviced abover this # of users
Xmanager server	 # email address of the manager of the system
Xpassword ellines # password for the 'restart', 'shutdown' and 'execute' requests
Xcomment server	 #Actual comment must be preceded by a pound sign
Xcomment ermis	 #Same here
Xcomment venus	 #Venus Distribution List
Xfrequency 5	 # how often to read mail (in seconds >= 0)
Xprecedence bulk	 # Usually bulk, junk, first-class or none
Xlimit message 65536	 # reject messages longer than 65536 bytes
X#limit files 65536	 # split files over this limit
Xoption sysv_ps	 # forces use of the SYS V ps command; other choice is bsd_ps
Xoption bsd_mail	 # define it if BSD mail is available
Xoption post_mail # Post to news; other choice is gate_mail
X#option relaxed_syntax	# listserv does not complain for extra params to
X			# requests
X#option ignore_invalid_requests		# ignore invalid user requests
Xmailmethod system
X#mailmethod env_var LOGNAME /bin/rmail	# Valid 'mailmethod' options:
X					# telnet
X					# sendmail /usr/lib/sendmail -ba
X					# sendmail /usr/lib/sendmail -t (IDA)
X					# rmail /bin/rmail
X					# binmail
X					# env_var VARIABLE /bin/mail
X					# env_var VARIABLE /usr/lib/sendmail -t
X					# env_var VARIABLE /bin/rmail
X					# env_var VARIABLE /usr/bin/mail
X#option bad_telnet # use this flag when a telnet session is not terminating
X
X# IMPORTANT NOTE:
X#
X# listserv also recognizes the requests 'restart' and 'shutdown' followed by
X# a password, that can be used to remotely restart and shutdown the server
X# system. The password is defined in the 'config' file using the 'password'
X# directive followed by the actual password.
X# This feature is not documented in the man page for obvious reasons.
X# The same password is used for the 'execute' request.
X
X# Below are some sample remote lists
Xremote rlist1 rlist1@remote-domain listserver@remote-domain 127.0.0.1 &
X  #Sex issues
Xremote rlist-2 rlist-2@remote-domain listserver@remote-domain &
X  #Fruit punch issues!!
Xremote cook cook@foo.com listserver@foo.com bar.foo.com 372	 &
X  #Cooking issues
*-*-END-of-config-*-*
echo x - .awk
sed 's/^X//' >.awk <<'*-*-END-of-.awk-*-*'
X{
X  tabcol = 40;
X  l = 0;
X  if (length ($1) < tabcol)
X    l = int ((tabcol + 7 - length ($1)) / 8);
X  printf "%s", $1;
X  k = 8 - int ((length ($1) % 8));
X  for (i = 0; i < k; i++) printf (" ");
X  for (i = 0; i < l; i++) printf "        ";
X  if ($2 != "") printf " %s", $2;
X  if ($3 != "") printf " %s", $3;
X  if ($4 != "") printf " %s", $4;
X  if ($5 != "") printf " %s", $5;
X  if ($6 != "") printf " %s", $6;
X  print "";
X}
*-*-END-of-.awk-*-*
echo x - .stats
sed 's/^X//' >.stats <<'*-*-END-of-.stats-*-*'
X#!/bin/sh
X# For NeXT hosts replace cut with awk.
X
Xpth=$1
Xsubscribers=$2
Xheaders=$3
Xoutfile=$4
Xheaderscp1=/tmp/.hdr3.$$
Xheaderscp2=/tmp/.hdr4.$$
Xshift; shift; shift; shift
Xif [ $# = 0 -o "$1" = "" ]; then
X  if [ `cut -d" " -f1,4 $subscribers | grep -i ' NO$' | \
X	cut -d" " -f1 > emails` ]; then
X    echo $0: syntax error lines 12-13 >> $pth/.warning
X    exit 1
X  fi
Xelif [ "$1" = " -ALL " ]; then
X  cp $headers $headerscp1
X  grep -i ' YES ' $subscribers | cut -d" " -f1 | 
X    ( x="garbage"
X      reduced=3
X      while [ "$x" != "" ]; do
X	read x
X	if [ "$x" != "" ]; then
X	  grep -i -v "^$x" $headerscp1 > $headerscp2
X	  hdr=$headerscp1
X	  headerscp1=$headerscp2
X	  headerscp2=$hdr
X	  if [ $reduced -eq 3 ]; then
X	    reduced=4
X	  else
X	    reduced=3
X	  fi
X	fi
X      done
X      exit $reduced
X    )
X  reduced=/tmp/.hdr$?.$$
X  if [ ! -f $reduced ]; then
X    echo $0: cannot open $reduced >> $pth/.warning
X   exit 1
X  fi
X  if [ `cat $reduced | tr '[a-z]' '[A-Z]' | sort | uniq -c | \
X	awk '{ print $2 " " $1 }' - | awk -f $pth/.awk - >> $outfile` ]; then
X    echo $0: syntax error lines 43-44 >> $pth/.warning
X    exit 1
X  fi
X  echo Total number of messages on file: `cat $headers | wc -l` >> $outfile
X  rm -f $headerscp1 $headerscp2
X  exit 0
Xelse
X  for user 
X  do
X    echo "$user" >> emails
X  done
Xfi
Xif [ `cat emails | $pth/.grep $headers > counts` ]; then
X  echo $0: syntax error line 57 >> $pth/.warning
X  exit 1
Xfi
Xif [ `paste emails counts | awk -f $pth/.awk - >> $outfile` ]; then
X  echo $0: syntax error line 61 >> $pth/.warning
X  exit 1
Xfi
Xecho Total number of messages on file: `cat $headers | wc -l` >> $outfile
Xrm -f counts emails 
Xexit 0
*-*-END-of-.stats-*-*
echo x - .grep
sed 's/^X//' >.grep <<'*-*-END-of-.grep-*-*'
X#!/bin/sh
Xf="k"
Xwhile [ "$f" != "" ]; do
X  read f
X  if [ "$f" != "" ]; then
X    grep -i "^$f" $1 | wc -l
X  fi
Xdone
*-*-END-of-.grep-*-*
echo x - .ignored
sed 's/^X//' >.ignored <<'*-*-END-of-.ignored-*-*'
X^server$
X^bin
X^sys
*-*-END-of-.ignored-*-*
echo x - queued
sed 's/^X//' >queued <<'*-*-END-of-queued-*-*'
X#!/bin/sh
X#
X# queued version 2.1
X#
X# Usage: queued <frequency>
X#
X# Daemon to process the mail queue periodically. The frequency is given
X# in seconds.
X
Xif [ $# -ne 1 ]; then
X  echo Usage: `basename $0` \<frequency\>
X  exit 3
Xfi
X
Xtrap 'rm -f $PID_QUEUED; kill -9 $$' 1 2 3 6
XPID_QUEUED=/usr/server/.pid.queued
Xwhile [ -f $PID_QUEUED ]; do	# kill previous queued
X  cat $PID_QUEUED | (read PID; kill -2 $PID > /dev/null 2>&1)
X  if [ $? -gt 0 ]; then
X    rm -rf $PID_QUEUED
X  fi
X  sleep 10
Xdone
Xecho $$ > $PID_QUEUED
X
XDIR=/usr/server/mqueue
XFREQ=$1
XMANAGER=`grep "^manager " /usr/server/config | awk '{ print $2 }'`
Xshift
Xif [ ! -d $DIR ]; then
X  echo $DIR directory missing
X  exit 3
Xfi
XMANAGER=`grep "^manager " /usr/server/config | awk '{ print $2 }'`
Xcd $DIR
XPWD=`pwd`
Xwhile [ 007 ]; do
X  for i in `\ls`
X  do
X    if [ ! -f /usr/server/pqueue ] && [ "$MANAGER" != "" ]; then
X      echo $0 aborts: pqueue not found | /bin/rmail $MANAGER
X      exit 1
X    fi
X    /usr/server/pqueue -e $PWD/$i
X    exit_status=$?
X    if [ $exit_status -gt 0 ]; then
X      echo $0 aborts: pqueue exit status $exit_status | /bin/rmail $MANAGER
X      exit $exit_status
X    fi
X  done
X  sleep $FREQ
Xdone
Xexit 0
*-*-END-of-queued-*-*
echo x - owners
sed 's/^X//' >owners <<'*-*-END-of-owners-*-*'
X# Example onwers file
X#
X# Syntax: email-address <list-alias | server> [preferences]
X#
X# valid preferences for owners are:
X#  CCSET, CCSUBSCRIBE, CCUNSUBSCRIBE, CCRECIPIENTS, CCINFORMATION,
X#  CCSTATISTICS, CCPRIVATE, CCRUN, CCERRORS, CCALL
X#
X# valid preferences for the manager are:
X#  CCGET, CCINDEX, CCLISTS, CCRELEASE, CCHELP, CCERRORS, CCALL
X#
X# Notice: only the primary of each list may be copied
X
Xtasos server CCERRORS CCLISTS
Xtasos ermis CCERRORS CCSUBSCRIBE CCSET
Xtasos venus CCALL
Xtasos herc
*-*-END-of-owners-*-*
echo x - unwanted.hosts
sed 's/^X//' >unwanted.hosts <<'*-*-END-of-unwanted.hosts-*-*'
X#
X# Sample unwanted.hosts file. List here all host names, IP addresses and/or
X# aliases which are not allowed to connect to your IUL server.
X# Each line may have multiple entries.
X#
X# If you want to allow access to some predetermined hosts only, create the file
X# priv.hosts (under this directory) and put the privileged host names and/or
X# IP addresses in it, in the same format as this file. priv.hosts has higher
X# priority than unwanted.hosts, i.e. access will be allowed to clients
X# connecting only from hosts listed in priv.hosts
X#
X# For example, to allow access to machines on your local network only, you
X# may do:
X#
X# % ln -s /etc/hosts priv.hosts
X
Xlocalhost
X127.0.0.1
X128.197.10.202  csa csa.bu.edu
*-*-END-of-unwanted.hosts-*-*
echo x - setup
sed 's/^X//' >setup <<'*-*-END-of-setup-*-*'
X#!/bin/sh
X# setup [path-to-unproto]
X#
X# List server system set up script. All you need to do is adjust the
X# Makefile in the src directory and run this script. You may specify
X# the directory where the unproto executable resides as an argument.
X# Also, read the documentation for setting up the /usr/server link.
X
Xecho
Xecho \	\	\	UNIX ListServer system setup script
Xecho \	\	\	-----------------------------------
Xecho
Xecho \	\	\	\	\ \ \ \ Version 6.0
Xecho
X
X# Determine how echo suppresses new-line
Xecho "a\c" > /tmp/echo
Xif [ "`grep c /tmp/echo`" = "a\c" ]; then
X  n='-n'
X  c=''
Xelse
X  n=''
X  c='\c'
Xfi
Xrm /tmp/echo
X
X# Extract the HOMEDIR definition from the Makefile
Xif [ ! -f src/Makefile ]; then
X  echo No src/Makefile found\; cannot proceed.
X  exit 2
Xfi
XHOMEDIR=`sed -n 's/^[	 ]*HOMEDIR[	 ]*=[	 ]*//p' src/Makefile`
X
X# Extract the current HOMEDIR
Xif [ ! -f src/ansi/defs.h ]; then
X  echo File src/ansi/defs.h not found\; cannot proceed.
X  exit 2
Xfi
XCURRDIR=`sed -n 's/^#[ 	]*define[	 ]*PATH[	 ]*\"//p' \
Xsrc/ansi/defs.h | tr -d '"'`
X
Xif [ ! -d $HOMEDIR ]; then
X  echo $HOMEDIR: not a valid path\; cannot proceed.
X  if [ "$HOMEDIR" != "/usr/server" ]; then
X    echo Either create the /usr/server link to point to $HOMEDIR, or 
X  fi
X  echo redefine HOMEDIR in src/Makefile to be `pwd`.
X  exit 2
Xfi
X
Xif [ "$HOMEDIR" != "$CURRDIR" ]; then
X  echo The system will now be built under $HOMEDIR,
X  echo as requested in src/Makefile\; if you want to build it under another \
Xdirectory
X  echo you may alter the HOMEDIR path in src/Makefile now.
X  echo $n "Proceed [y]? $c"
X  read x
X  if [ "$x" = "n" ] || [ "$x" = "N" ]; then
X    exit 0
X  fi
X  for i in flocks queued peer news flocks config help/TOPICS help/listserv \
X    src/defs.h src/ansi/defs.h src/nonansi/defs.h \
X    `find . -name DIR -print` `find . -name INDEX -print`; do
X    grep -s $CURRDIR $i > /dev/null
X    if [ $? -eq 0 ]; then
X      echo Remaking $i
X      sed s@$CURRDIR@$HOMEDIR@g $i > ${i}~
X      test -s ${i}~ && mv ${i}~ $i
X     fi
X  done
Xelse
X  echo System already built under $CURRDIR
Xfi
X
Xcd $HOMEDIR
XPWD=`pwd`
Xexport PWD
Xif [ ! -d lists ]; then
X  echo Creating directory `pwd`/lists
X  mkdir lists
Xfi
Xif [ ! -d mqueue ]; then
X  echo Creating directory `pwd`/mqueue
X  mkdir mqueue
Xfi
Xif [ -f /usr/include/sys/select.h ]; then
X  echo Compile with -DHAVE_SELECT_H if you already have not done so.
Xelse
X  echo Looks like you cannot compile with the -DHAVE_SELECT_H flag\; run systest.
Xfi
Xif [ -f /usr/include/sys/ulimit.h -o -f /usr/include/ulimit.h ]; then
X  echo Compile with -DHAVE_ULIMIT_H if you already have not done so.
Xelse
X  echo Looks like you cannot compile with the -DHAVE_ULIMIT_H flag\; run systest.
Xfi
Xif [ -f /usr/include/setjmp.h ]; then
X  echo Compile with -DHAVE_SETJMP_H if you already have not done so.
Xelse
X  echo Looks like you cannot compile with the -DHAVE_SETJMP_H flag\; run systest.
Xfi
Xif [ -f /usr/include/tzfile.h ]; then
X  echo Compile with -DHAVE_TZFILE_H if you already have not done so.
Xelse
X  echo Looks like you cannot compile with the -DHAVE_TZFILE_H flag\; run systest.
Xfi
Xif [ -f /usr/include/syslog.h ]; then
X  echo 'You may wish to compile with -DSYSLOG=facility to use syslog(3) for reports.'
Xfi
Xif [ ! -f /usr/include/netdb.h ]; then
X  echo Looks like netdb.h is missing\; run systest.
Xfi
Xif [ -f /usr/include/sys/wait.h ]; then
X  grep waitpid /usr/include/sys/wait.h > /dev/null 2>&1
X  if [ $? -gt 0 ]; then
X    echo WARNING: Could not find waitpid\(\) in /usr/include/sys/wait.h
X    echo If you do not have this function nor wait3\(\), undefine the symbol
X    echo GO_INTERACTIVE in src/defs.h\; if you have wait3\(\), see serverd.c to
X    echo see how it is used on systems that have wait3\(\) only.
X  fi
Xfi
Xif [ ! -r /usr/lib/news/inews ]; then
X  echo WARNING: /usr/lib/news/inews not a valid path\; consult the documentation
Xfi
Xif [ $# = 0 ]; then
X  echo $n "Do you wish to use unproto for compilation [n]? $c"
X  read x
X  if [ "$x" = "y" ] || [ "$x" = "Y" ]; then
X    dir=`which unproto > /tmp/unproto 2>&1`
X    udir=''
X    egrep 'no unproto in|unproto not in' /tmp/unproto > /dev/null 2>&1
X    if [ $? -gt 0 -a "$dir" != "" ]; then
X      udir=[$dir ]
X    fi
X    rm -f /tmp/unproto
X    echo $n "Enter the directory unproto resides: $udir$c"
X    read x
X    PATH=$PATH:$x
X    USE_UNPROTO=1
X    export PATH USE_UNPROTO
X  fi
Xelse
X  PATH=$PATH:$1
X  export PATH
Xfi
Xcd src
Xmake -f Makefile
Xif [ $? -gt 0 ]; then
X  echo $0: error\(s\) during compilation\; quitting
X  exit 1
Xfi
Xmake -f Makefile install -i
Xif [ $? -gt 0 ]; then
X  echo $0: error during installation\; quitting
X  exit 1
Xfi
Xcd ..
Xif [ ! -f start ]; then
X  echo start not found
X  exit 2
Xfi
Xif [ ! -f serverd ]; then
X  echo serverd not found
X  exit 2
Xfi
Xif [ ! -f list ]; then
X  echo list not found
X  exit 2
Xfi
Xif [ ! -f listserv ]; then
X  echo listserv not found
X  exit 2
Xfi
Xif [ ! -f catmail ]; then
X  echo catmail not found
X  exit 2
Xfi
Xif [ ! -f rev ]; then
X  echo rev not found
X  exit 2
Xfi
Xif [ ! -f fwin ]; then
X  echo fwin not found
X  exit 2
Xfi
Xif [ ! -r config ]; then
X  echo config not found
X  exit 2
Xfi
Xif [ ! -r .awk ]; then
X  echo .awk not found
X  exit 2
Xfi
Xif [ ! -r .stats ]; then
X  echo .stats not found
X  exit 2
Xfi
Xif [ ! -r .grep ]; then
X  echo .grep not found
X  exit 2
Xfi
Xif [ ! -r .ignored ]; then
X  echo .ignored not found
X  exit 2
Xfi
Xif [ ! -r owners ]; then
X  echo owners not found
X  exit 2
Xfi
Xtouch requests batch owners unwanted.hosts
Xchmod 755 serverd > /dev/null 2>&1
Xchmod 700 start list listserv .awk .stats .grep ulock redux upgrade_to_6.0 \
Xpeer news queued pqueue farch rev fwin tlock archives doc help mqueue src lists
Xchmod a+rx catmail
Xchmod u+s catmail
Xchmod 711 . src
Xchmod 660 requests
Xchmod 600 config owners unwanted.hosts batch mbox sent received flocks \
X> /dev/null 2>&1
Xecho
Xecho Now serverd will have to be setuid to root if it is to run in interactive \
Xmode
Xecho and use a privileged port. Please enter the root password below if so:
Xsu root -c 'chown root serverd src/serverd; chmod u+s serverd'
Xif [ $? -gt 0 ]; then
X  echo serverd unchanged
Xfi
Xecho
Xecho NOTICE: the canned archives/iul directory contains everything a user \
Xwill need
Xecho for live connections\; you may wish to copy it to your own archives now.
Xecho
Xif [ -f upgrade_to_6.0 ]; then
X  echo Finally, if this is an upgrade, run the script upgrade_to_6.0
Xfi
Xecho List Server System ready.
Xexit 0
*-*-END-of-setup-*-*
echo x - help/TOPICS
sed 's/^X//' >help/TOPICS <<'*-*-END-of-help/TOPICS-*-*'
Xapprove		/usr/server/help/approve
Xdiscard		/usr/server/help/discard
Xedit		/usr/server/help/edit
Xfax		/usr/server/help/fax
Xgeneral		/usr/server/help/general
Xget		/usr/server/help/get
Xindex		/usr/server/help/index
Xinformation	/usr/server/help/information
Xlists		/usr/server/help/lists
Xlistserver	/usr/server/help/listserv
Xlive		/usr/server/help/live
Xput		/usr/server/help/put
Xrecipients	/usr/server/help/recipients
Xreview		/usr/server/help/recipients
Xrelease		/usr/server/help/release 
Xreports		/usr/server/help/reports
Xrun		/usr/server/help/run
Xsearch		/usr/server/help/search
Xset		/usr/server/help/set
Xstatistics	/usr/server/help/statistics 
Xsubscribe	/usr/server/help/subscribe
Xunsubscribe	/usr/server/help/unsubscribe
Xsignoff		/usr/server/help/unsubscribe
Xwhich		/usr/server/help/which
*-*-END-of-help/TOPICS-*-*
echo x - help/approve
sed 's/^X//' >help/approve <<'*-*-END-of-help/approve-*-*'
XSyntax: approve <list> <password> <tag>
X	Each time a new message arrives for a moderated list, a copy is
X	sent to the owner soliciting his approval, with proper instructions
X	for approving or discarding the message. Each such message is
X	identified by a unique tag number. If the message is approved for
X	posting, the owner sends an 'approve' request providing the proper
X	tag number.
X
X	Messages neither approved nor discarded are not removed from the spool.
*-*-END-of-help/approve-*-*
echo x - help/discard
sed 's/^X//' >help/discard <<'*-*-END-of-help/discard-*-*'
XSyntax: discard <list> <password> <tag>
X	Each time a new message arrives for a moderated list, a copy is
X	sent to the owner soliciting his approval, with proper instructions
X	for approving or discarding the message. Each such message is
X	identified by a unique tag number. If the message is not approved for
X	posting, the owner sends a 'discard' request providing the proper
X	tag number.
X
X	Messages neither approved nor discarded are not removed from the spool.
*-*-END-of-help/discard-*-*
echo x - help/edit
sed 's/^X//' >help/edit <<'*-*-END-of-help/edit-*-*'
XSyntax: edit <list> <password> <file>
X	The specified file for the specified list will be sent. To be used
X	in conjunction with the 'put' request.
X
X	This request is for list administration and is issued by the
X	list's owner. Contact the system's manager for more information.
X
*-*-END-of-help/edit-*-*
echo x - help/fax
sed 's/^X//' >help/fax <<'*-*-END-of-help/fax-*-*'
XSyntax: fax <fax-number> <archive | path-to-archive> <file> [/password] [parts]
X	Have the specified file from the specified archive faxed to the
X	specified number. The file may have been split into parts because some
X	mailers cannot send large files, and in this case you will receive as
X	many faxes as the number of parts the file has been split into. Make
X	sure you do not request binary files or tar files as they will be
X	uuencoded first and will be probably totally useless on paper.
X
X	If certain parts are specified, then only these parts (if they exist)
X	will be faxed. The parts are specified as numbers separated by spaces.
X	You may not specify ranges of parts (e.g. 1-3).
X
X	Certain archives may be private, in which case you have to specify
X	a password (the slash is required) in order to obtain files from
X	them. Different archives may have different passwords, but all files
X	from the same archive may be obtained with the same password.
X
X	Archives may have the same names; they can be distinguished by the
X	path (the branch in the hierarchy) to them. For example, the archives
X	unix and pub/unix are distinct. 'path-to-archive' is a UNIX style
X	path (such as pub/unix) -- i.e. a '/' is used to move through the
X	branches of the hierarchy. An 'index' request always reports paths
X	to archives for your convinience.
X
X	This request is honored only if received by email.
*-*-END-of-help/fax-*-*
echo x - help/general
sed 's/^X//' >help/general <<'*-*-END-of-help/general-*-*'
X			  UNIX ListServer 6.0
X
XHere is a brief description of the set of requests recognized by UNIX
XListServer. Everything appearing in [] below is optional; everything appearing
Xin <> is mandatory; all arguments are case insensitive. The vertical bar ("|")
Xis used as a logical OR operator between the arguments. Requests may be
Xabbreviated, but you must specify at least the first three characters.
X
XKeep in mind that when referring to a <list>, that list may be of two kinds:
Xlocal or remote, unless otherwise noted. When referring to a local list, your
Xrequest will be immediately processed; when referring to a remote list (a list
Xserved by another ListServer which this system knows about), your request will
Xbe appropriately forwarded. Issue a 'lists' request to get a listing of all
Xlocal and known remote lists to this ListServer.
X
XRecognized requests are:
X
X
Xhelp [topic]
X--------------
XWithout arguments, this file. Otherwise get specific information on the
Xselected topic. Topics may also refer to requests. To learn more about this
Xsystem issue a 'help listserver' request. To get a listing of all available
Xtopics, generate an error message by sending a bogus request like 'help me'.
X
X
Xset <list> [<option> <arg[s]>]
X-----------------------------
XWithout the optional arguments, get a list of all current settings for
Xthe specified list. Otherwise change the option to a new value for that
Xlist. Issue a 'help set' request for more information.
X
X
Xsubscribe <list> <your name>
X----------------------------
XThe only way to subscribe to a list. 
X
X
Xunsubscribe <list>
Xsignoff <list>
X------------------
XTwo ways of removing yourself from the specified list.
X
X
Xrecipients <list>
Xreview <list>
X-----------------
XGet a listing of all non-concealed people subscribed to the specified list.
X
X
Xinformation <list>
X------------------
XGet information about the specified list.
X
X
Xstatistics <list> {[subscriber email address(es)] | [-all]}
X-----------------------------------------------------------
XGet a listing of non-concealed subscribers along with the number of messages
Xeach one of them has sent to the specified list. If the optional email addresses
Xare given, then statistics will be collected for these users only. For example:
X		stat foo user1@domain user2@domain
Xwill generate statistics about these two subscribers. "-all" lists statistics
Xfor all users that have posted on the list (whether currently subscribed ot
Xnot).
X
Xrun <list> [<password> <cmd [args]>]
X-------------------------------------
XRun the specified command with the optional arguments and receive the output
Xfrom stdout and/or stderr. To get a listing of all available commands to run,
Xomit the arguments, i.e. issue a 'run <list>' request. You have to belong to
Xthe specified list, and must have obtained the password from the list's owner;
Xthe owner's address may be found in the Errors-To: header line of each
Xdelivered message. <list> may be local only.
X
X
Xlists
X-----
XGet a list of all local mailing lists that are served by this server, as well
Xas of all known remote lists.
X
X
Xindex [archive | path-to-archive] [/password] [-all]
X----------------------------------------------------
XGet a list of files in the selected archive, or the master archive if no
Xarchive was specified. If an archive is private, you have to provide its
Xpassword as well.
X
X
Xget <archive | path-to-archive> <file> [/password] [parts]
X----------------------------------------------------------
XGet the requested file from the specified archive. Files are usually split in
Xparts locally, and in such a case you will receive the file in multiple email
Xmessages -- an 'index' request tells you how many parts the file has been split
Xinto, and their sizes; if you need to obtain certain parts, specify them as
Xoptional arguments. If an archive is private, you have to provide its password
Xas well.
X
Xsearch <archive | path-to-archive>] [/password] [-all] <pattern>
X----------------------------------------------------------------
XSearch all files of the specified archive (and all of its subarchives if -all
Xis specified) for lines that match the pattern. The pattern can be an
Xegrep(1)-style regular expression with support for the following additional
Xoperators: '~' (negation), '|' and '&' (logical OR and AND), '<' '>' (group
Xregular expressions). The pattern may be enclosed in single or double quotes.
XNote: . matches any character including new line.
X
Xfax <fax-number> <archive | path-to-archive> <file> [/password] [parts]
X-----------------------------------------------------------------------
XSame as 'get', but it faxes you the files instead to the specified number.
X
Xrelease
X-------
XGet information about the current release of this ListServer system.
X
X
Xwhich
X-----
XGet a listing of local mailing lists to which you have subscribed.
X
X===============================================================================
X
XThe requests below are list owner specific. List owners should contact the
Xsystem's manager for extensive usage information:
X
X
Xreports <list> <password>
X-------------------------
XObtain all reports about the specified local list.
X
X
Xedit <list> <password> <file>
X-----------------------------
XObtain the specified file for editing.
X
X
Xput <list> <password> <keyword> [args]
X--------------------------------------
XManipulate addresses and change system files.
X
X
Xapprove <list> <password> <tag>
X-------------------------------
XApprove the message identified by the tag number for posting to the
Xspecified moderated list.
X
X
Xdiscard <list> <password> <tag>
X-------------------------------
XDiscard the message identified by the tag number sent to the specified
Xmoderated list.
X===============================================================================
X
XIn addition, help is available on the following topic(s):
X
Xlive
X----
XLearn how to connect to this ListServer over the Internet for live processing of
Xyour requests.
X
Xlistserver
X----------
XLearn more about this system and list management software in general.
*-*-END-of-help/general-*-*
echo x - help/get
sed 's/^X//' >help/get <<'*-*-END-of-help/get-*-*'
XSyntax: get <archive | path-to-archive> <file> [/password] [parts]
X	Get the specified file from the specified archive. The file may
X	have been split into parts because some mailers cannot send large
X	files, and in this case you will receive as many messages as the
X	number of parts the file has been split into. Binary files are
X	uuencoded, so you will need to combine all the parts sent and
X	uudecode. The requested file may be a tar file (containing other
X	files and perhaps directories) in which case when you uudecode,
X	a tar file will be created and you will need to run the UNIX
X	utility 'tar' on it to extract the files and directories.
X
X	If certain parts are specified, then only these parts (if they exist)
X	will be sent. The parts are specified as numbers separated by spaces.
X	You may not specify ranges of parts (e.g. 1-3).
X
X	Certain archives may be private, in which case you have to specify
X	a password (the slash is required) in order to obtain files from
X	them. Different archives may have different passwords, but all files
X	from the same archive may be obtained with the same password.
X
X	Archives may have the same names; they can be distinguished by the
X	path (the branch in the hierarchy) to them. For example, the archives
X	unix and pub/unix are distinct. 'path-to-archive' is a UNIX style
X	path (such as pub/unix) -- i.e. a '/' is used to move through the
X	branches of the hierarchy. An 'index' request always reports paths
X	to archives for your convinience.
*-*-END-of-help/get-*-*
echo x - help/index
sed 's/^X//' >help/index <<'*-*-END-of-help/index-*-*'
XSyntax: index [archive | path-to-archive] [/password] [-all]
X	Get a list of files for the specified archive, or listserver -- the
X	master archive, if none given. Files are actually listed for
X	the requested archive, plus all of its subarchives but only if you
X	provide the -all option. Obviously the master archive will list all
X	of the available files from this server.
X
X	Certain archives may be private, and in this case you have to
X	specify a password for accessing them. The slash is required.
X	Different archives may have different passwords.
X
X	If you do not specify an archive but provide a password, then
X	only public and private archives that match this password will
X	be listed.
X
X	The archiving system is hierarchical. Therefore subarchives
X	may have the same names; they can be distinguished by the path
X	(the branch in the hierarchy) to them. For example, the archives
X	unix and pub/unix are distinct. 'path-to-archive' is a UNIX style
X	path (such as pub/unix) -- i.e. a '/' is used to move through the
X	branches of the hierarchy. An 'index' request always reports paths
X	to archives for your convinience.
*-*-END-of-help/index-*-*
echo x - help/information
sed 's/^X//' >help/information <<'*-*-END-of-help/information-*-*'
XSyntax: information <list>
X        Get information about the specified list. This usually includes
X	its purpose and certain restrictions for posting messages.
*-*-END-of-help/information-*-*
echo x - help/lists
sed 's/^X//' >help/lists <<'*-*-END-of-help/lists-*-*'
XSyntax: lists
X        Get a list of all mailing lists served by this server; the list
X	consists of email addresses and a brief comment for each one.
X
X	In addition, any remote lists (lists served by remote servers
X	that are known to this system) are also listed. Requests for these
X	remote lists may be sent to this ListServer and they will be
X	forwarded for you to the proper server.
*-*-END-of-help/lists-*-*
echo x - help/listserv
sed 's/^X//' >help/listserv <<'*-*-END-of-help/listserv-*-*'
X#! /bin/sh
XLISTSERVER=`egrep '^[ 	]*server[ 	]+' /usr/server/config | awk '{ print $2 }'`
Xcat <<EOF
X			UNIX ListServer version 6.0
X
X			        March 1993
X
X			  Preliminary User's Guide
X
XContents:
X---------
X1) What is UNIX ListServer
X2) How do I use the ListServer
X   2.1) Regular users
X   2.2) List owners
X3) Archives
X4) Format of requests
X5) More information
X
X
X1) What is UNIX ListServer
X--------------------------
XUNIX ListServer is a system that automates mailing lists and archives. Users
Xmay add themselves to mailing lists supported by ListServer, post messages,
Xreview the members of a list, etc. The system also maintains hierarchical
Xarchives of files that users may get. Archives and mailing lists may be private,
Xwhich means that a password is required for archive access, and that the owner
Xof a private mailing list decides who may suscribe and who may not. Lists may
Xalso be moderated, in which case the owner decides which messages are made
Xpublic, and which are flushed.
X
XLISTSERV is a system that was originally designed (and still updated) by Eric
XThomas (who first coined the term LISTSERV) for BITNET nodes and extensive help
Xon that system is available from your favorite BITNET node (uga.cc.uga.edu for
Xexample; send email to listserv@uga.cc.uga.edu for more information). This
Xsystem, written by Tasos Kotsikonas and a group of current users, certainly
Xexhibits its influence from the BITNET system as can be seen from the names of
Xuser requests and their syntax. Otherwise, no connection exists between the two
Ximplementations, and certainly this is not a port of the BITNET Listserv.
X
X2) How do I use the ListServer
X------------------------------
XThere are two levels of users: a) regular users who subscribe to lists and
Xsend/receive messages and get files from archives, and b) list owners who
Xoversee the functioning of lists. There is of course a system manager that
Xoversees the entire operation.
X
XList owners have certain privileges: they can add and remove users from their
Xlists, reject postings to moderated lists, manipulate addresses, etc.
X
XAll users interact with ListServer by email. ListServer understands a very
Xlimited set of requests and expects a fixed syntax for each one of those. Users
Xsend email to $LISTSERVER when they wish to have a request processed. If
Xusers want to send a message to a mailing list, they email to list-name@address
Xinstead.
X
X2.1) Regular users
X------------------
XUsually, the first thing that a regular user will want to do is find out which
Xlists are available for subscription. He should then send email to
X$LISTSERVER with a 'lists' request in the body of the message and nothing
Xelse.
X
XHe then subscribes to a list called foo by sending another email to
X$LISTSERVER with the following request:
X
X		subscribe foo <His Name>
X
Xwhere <His Name> should let people know who he is.
X
XOnce subscribed, he may send messages to foo@address. He may remove himself
Xfrom that list later on by issuing a:
X
X		unsubscribe foo
X
Xrequest to $LISTSERVER.
X
XTo access the archives, an 'index' request is sent to $LISTSERVER to find
Xout what archives are available and what files may be obtained from these
Xarchives. A user then may request one or more files via a 'get' request. For
Xexample:
X
X	get products/database/name/version/fixes patch
X
Xwill send the file patch from that archive. In the case of binary files, what
Xthe user receives is a uuencoded file, possibly split in parts due to its size.
XArchives may be searched with the 'search' request which accepts an egrep(1)-
Xstyle pattern,
X
XFor extensive information on all requests, $LISTSERVER accepts requests
Xlike 'help [topic]' where 'topic' is a request.
X
X2.2) List owners
X----------------
XList owners are users who have special privileges on the lists they are
Xassigned to. Each mailing list has at leat one primary owner and may have
Xinfinite secondary ones. The primary owner will be the recipient of all error
Xmessages and may set more preferences by contacting the system's manager;
Xthese preferences include requests to be copied on certain user requests. For
Xexample, an owner may wish to be notified when someone subscribed to his list,
Xor when someone signed off.
X
XEach list is accessed by an owner via a password. All owner requests require
Xthat password. In addition, the email address of the owner is verified for
Xsuch requests.
X
XThe following owner requests (which should always be sent to $LISTSERVER)
Xare recognized (refer to the man page for hidden owner requests not mentioned
Xhere):
X
Xa)	reports <list> <password>
X
XA copy of the report files on list activity are sent to the owner.
X
Xb)	edit <list> <passwrod> <file>
X
XEach list is controlled through some system files. These include the
Xsubscribers file, the welcoming message, the aliases file and the ignored file
X(enemies of the list). Refer to the man page for more information.
X
Xc)	put <list> <password> <keyword> [args]
X
XThe aboved edited files may be put back using this request. The owner may also
Xput new aliases and enemies to the database. Refer to the man page for more
Xinformation.
X
Xd)	approve <list> <password> <tag>
X
XIf a list is moderated, each message sent by a regular user is forwarded to
Xthe primary owner for review, editing and approval. Each such message is
Xidentified by a unique tag number. The owner sends back the approved message
Xto $LISTSERVER preceded by the above request, and provides the proper tag
Xnumber.
X
Xe)	discard <list> <password> <tag>
X
XThis is the opposite of 'approve'. A message that is not approved or discarded
Xwill remain in the queue for ever.
X
X3) Archives
X-----------
XEach mailing list may have its messages automatically archived. This can be
Xuseful for newcomers who want to catch up with the discussion, or for posting
Xand archiving FAQ's.
X
XThe system may also keep archives of patches, and other types of files. As
Xexplained before, archives may be private in which case passwords are required
Xfor access for both the 'index' and 'get' requests.
X
X4) Format of requests
X---------------------
XFor the most part, requests span a single line. However, for whatever the
Xreason a request may be continued on another line if it is terminated by &\n
X(ambersand followed by carriage return).
X
XRequests like 'put' may take a file as argument (like the subscribers file, or
Xthe welcoming message). In these cases, the actual text follows the 'put'
Xrequest and spans till the end of the message. For example, here is a session
Xwhere the owner would put the welcoming message:
X
X% mail $LISTSERVER
XSubject: 
X
Xput my-list my-password welcome
X
XWelcome to the my-list list. You may sign off any time by sending the 
Xfollowing request to $LISTSERVER:
X
X	sign off my-list
X
XEnjoy!
X^D
X%
X
X5) More information
X-------------------
XSee server(1) for system setup and management.
XSee listserv(1) for list owner information on list management and their
Xprivileges.
XSee list(1) for information on moderated lists and how to define user aliases
Xand unwanted users. Also how to connect to news groups.
XSee listserv(1) for a complete description of all user requests.
XEOF
*-*-END-of-help/listserv-*-*
echo x - help/live
sed 's/^X//' >help/live <<'*-*-END-of-help/live-*-*'
X#!/bin/sh
XHOST=`hostname`
XADDRESS=`egrep "[0-9][ 	]+$HOST[ 	]*" /etc/hosts | head -1 | awk '{ print $1 }'`
XHOSTNAME=`nslookup $ADDRESS | grep Name: | tr -d '\[\]' | awk '{ print $2 }'`
X#HOSTNAME=$HOST
Xcat <<EOF
X	How to connect and use the Interactive UNIX ListServer
X	------------------------------------------------------
X
XUNIX ListServer may be reached for interactive (direct, without using email)
Xsessions if both this system and your host are on Internet. Live sessions
Xlook a lot like ftp sessions, in the sense that the user may issue all of
Xthe valid requests he would send over email, plus download files from the
Xarchives. The server has also the capability of connecting to remote live UNIX
XListServers for requests that refer to remote lists.
X
XTo be able to connect to an IUL, you will need a program called "iul"; this
Xshould be available from this site's archives; issue an 'index' request and
X'get' all files from the designated archive.
X
XYou will need to compile the iul client before you use it; once compiled, you
Xmay use it as follows:
X
X			iul ${HOSTNAME:-this host's address} [port]
X
XAll compliant Interactive UNIX ListServers listen at port 372 for connections
Xby default, but they may use any other port, in which case you will have
Xto specify which one. For example, on host cs.bu.edu a sample server is
Xrunning for testing purposes; to connect to it you would issue the following
Xcommand:
X
X			iul cs.bu.edu 7000
X
XLog in with email address 'test' and password 'new-user' to obtain subscriber
Xprivileges (more on this later). Notice that you may also specify an I.P.
Xaddress instead of the DNS name of the host. If using domain names, you
Xprobably have to be running a name server on your local network.
X
XOnce a connection is established, you will be asked for an email address and a
Xpassword; these will be used to establish the privileges (valid set of requests)
Xupon login. A mismatch or no email address reduces these privileges to a
Xminimum (casual user).
X
XCasual users may only issue 'help', 'information', 'recipients' and 'statistics'
Xfor nonprivate lists, 'lists', 'index', 'get', 'search' and 'release' requests.
X
XSubscriber privileges also include the 'set', 'run', 'unsubscribe', and 'which'
Xrequests.
X
XOwners may, in addition, issue all of their administrative requests.
X
XTo gain subscriber privileges you must be subscribed to at least one list, use
Xyour email address as shown by a 'recipients' request, and provide the password
Xthat you have set before. If you are subscribed to more than one lists, any
Xpassword from the ones that you use for each list will do (assuming that your
Xsubscription address is the same for all those lists). However, if you belong to
Xa private list, you should use the password as set for that list in order to be
Xable to have access to 'recipients' and 'statistics' listings.
X
XTo gain owner privileges, please contact this system's manager (you may
Xget the manager's address via a 'release' request).
X
XTo end a live session, just type 'quit' or 'exit'. To review your privileges
Xat any time, type '?' or 'privileges'. Connections are limited to a certain
Xamount of time; to get the time left on your connection, type 'timeleft'.
X
XRequests may be continued on multiple lines by use of '&' followed by a new-line
Xcharacter. The output of every request (if it is not an error message) may be
Xredirected to a file by using a '>' followed by a file name.
X
XExample:
X
XREQUEST> index > index
X
Xor by using '>>' in which case the output is appended to the file. File transfer
Xalso takes place when you 'get' files from an archive. For example, if you
X
XREQUEST> get listserver info
X
Xthe output will be saved under file 'info'. You choose another file name by
Xredirecting:
X
XREQUEST> get listserver info > index
X
XThe keywords 'binary' and 'ascii' specify the transfer type; the default is
X'binary', i.e. files are transferred as they are; in ascii mode, new-line
Xcharacters are translated to carriage-return/line-feed pairs.
X
XInput may also be redirected from a file by using '<' followed by a file
Xname; for example:
X
XREQUEST> < batched.requests
X
XIn this case, each line will be interpeted as a separate request and replies
Xwill be produced. Here is an example batch file:
X
Xindex > index
Xget listserver info >> index
Xquit
X
XA particularly useful case of input redirection is the 'put' request:
X
XREQUEST> put ermis ermis1 welcome < prepared-welcome-file
X
XNote that for requests requiring a password, you may replace the password with
Xa dash (-) since you provided the password at login; this of course works only
Xif you use the same password for all lists you are subscribed to or own.
XThen, the previous request could have been written as:
X
XREQUEST> put ermis - welcome < prepared-welcome-file
X
XMoreover, the output of requests not redirected may be piped to UNIX command
Xusing the '|' character.  For example:
X
XREQUEST> review ermis | more
X
Xwill pipe the output of the 'review' request to the UNIX "more" command. As
Xanother example, to look for a particular subscriber in a list, one may do:
X
XREQUEST> review venus | grep -i Name
X
XAs a last note, requests for remote lists are serviced by contacting the remote
Xservers, if possible. Live requests are subject to the usual restrictions
X(system loads) -- requests that are designated by the manager for batch
Xprocessing will be rejected, and you will have to submit your request via email.
XEOF
*-*-END-of-help/live-*-*
echo x - help/put
sed 's/^X//' >help/put <<'*-*-END-of-help/put-*-*'
XSyntax: put <list> <password> <keyword> [args]
X	Manipulate user addresses (aliases, etc.) and change the
X	list's system files. When changing system files (such as the
X	aliases, ignored, subscribers, etc.), the text for these files
X	begins immediately after this request (therefore subsequent requests
X	are not processed, they are taken to be part of this file) and
X	spans through the end of your message -- physical end of message,
X	or the beginning of the signature line starting with "--" in a
X	single line (beware of a .signature file that does start with "--").
X	Take extra precaution when creating new system files; use the
X	correct syntax and keep in mind that the copy you send will
X	overwrite the current file.
X
X	Refer to the documentation for possible keywords and their
X	arguments.
X
X	This request is for list administration and is issued by the
X	list's owner. Contact the system's manager for more information
X	about the keywords and their possible arguments.
*-*-END-of-help/put-*-*
echo x - help/recipients
sed 's/^X//' >help/recipients <<'*-*-END-of-help/recipients-*-*'
XSyntax: recipients <list> (or: review <list>)
X        Get a listing of the current subscribers for the specified list;
X	each person's email address is followed by his/her name. If the list
X	is linked with any peer lists, your request will be forwarded to
X	the appropriate server(s) also, except when the request is live.
X
X	If a list is private, members only may issue this request.
*-*-END-of-help/recipients-*-*
echo x - help/release
sed 's/^X//' >help/release <<'*-*-END-of-help/release-*-*'
XSyntax: release
X	Get information about the current release of this ListServer system.
*-*-END-of-help/release-*-*
echo x - help/reports
sed 's/^X//' >help/reports <<'*-*-END-of-help/reports-*-*'
XSyntax: reports <list> <password>
X	All report files for the specified list will be sent. These
X	include the current and all previous ones since the list's
X	inception. Since these files continuously grow and tend to be large,
X	they are shrunk after processing this request. All in all, two mail
X	messages are sent.
X
X	This request is for list administration and is issued by the
X	list's owner. Contact the system's manager for more information.
*-*-END-of-help/reports-*-*
echo x - help/run
sed 's/^X//' >help/run <<'*-*-END-of-help/run-*-*'
XSyntax: run <list> [<password> <cmd [args]>]
X	Run the specified command (arguments may be optional) and receive the
X	output from stdout and/or stderr. You must be a member of the list,
X	and you must obtain the password and the commad's name and optional
X	arguments from the owner.
X
X	In its simplest form (run <list>), return all commands that may be
X	executed by this list's subscribers.
*-*-END-of-help/run-*-*
echo x - help/search
sed 's/^X//' >help/search <<'*-*-END-of-help/search-*-*'
XSyntax: search <archive | path-to-archive>] [/password] [-all] <pattern>
X	Search all files of the specified archive (and all of its subarchives
X	if -all is specified) for lines that match the pattern. The pattern
X	can be an egrep(1)-style regular expression with support for the
X	following additional operators: '~', if leading the regular expression
X	it reverses its meaning; '|' and '&' separate multiple regular
X	expressions (logical OR and AND); '<' '>' group regular expressions
X	(we preserve the meaning of the parentheses from ed(1), and remove the
X	meaning of < and > from ed(1) since in the ListServer context they are
X	either the default, or inappropriate). These can be used literally by
X	escaping them with '\'. In addition, the following characters should
X	be defined in matched pairs: (), <>, []. The pattern may be enclosed
X	in single or double quotes.
X
X	Pattern matching is case insensitive.
X
X	Certain archives may be private, and in this case you have to
X	specify a password for accessing them. The slash is required.
X	Different archives may have different passwords.
X
X	The archiving system is hierarchical. Therefore subarchives
X	may have the same names; they can be distinguished by the path
X	(the branch in the hierarchy) to them. For example, the archives
X	unix and pub/unix are distinct. 'path-to-archive' is a UNIX style
X	path (such as pub/unix) -- i.e. a '/' is used to move through the
X	branches of the hierarchy. An 'index' request always reports paths
X	to archives for your convinience.
X
X	Examples:
X
X	search listserver -all "<oranges &~apples>|.*andarin[sz]?"
X	search iul '\|.*\|.$'
X
X	Notes:
X	- . matches ANY character including newline, so to find all lines
X	  that contain a newline only, one should use '^.$' instead of '^$'
X	- If you do not quote your pattern, it will include any blanks you
X	  may have appended after it by mistake, and it will not include any
X	  blanks that may start the regular expression.
*-*-END-of-help/search-*-*
echo x - help/set
sed 's/^X//' >help/set <<'*-*-END-of-help/set-*-*'
XSyntax: set <list> [<option> <arg[s]>]
X	Without the optional arguments, get a list of all current settings for
X	the specified list. Otherwise change the option to the new value for
X	that list, as follows:
X
X        option can be: mail, password, address, conceal
X        arg for mail can be: ack, noack, postpone, digest
X	args for password have to be: current-password new-password
X	args for address have to be: current-password new-address
X	args for conceal can be: yes, no
X
X        'set <list>': get the current option settings for the list
X        'set <list> mail ack': your message is sent back to you
X        'set <list> mail noack': your message is not sent back to you
X        'set <list> mail postpone': no messages will be sent to you until you
X                change mode again
X	'set <list> mail digest': your message is not sent back to you.
X		New messages are not sent to you as they arrive, but are
X		accumulated into digests that are periodically sent to you.
X	'set <list> password <current-password> <new-password>': change your
X		password for live access to the system (i.e. for subscriber
X		access privileges when telneting to the system).
X	'set <list> address <current-password> <new-address>: change the address
X		you are subscribed with; may not be available on all lists.
X	'set <list> conceal yes': remove yourself from 'recipients' and
X		'statistics' listings.
X	'set <list> conceal no': opposite of previous mode.
*-*-END-of-help/set-*-*
echo x - help/statistics
sed 's/^X//' >help/statistics <<'*-*-END-of-help/statistics-*-*'
XSyntax: statistics <list> {[subscriber email address(es)] | [-all]}
X        Get statistical information for all subscribers, or for the specified
X        one(s) only, in the specified list. Each email address is followed
X	by a count of the number of messages sent to this list by this user
X	to date. Wild characters are allowed when inquiring about specific
X	subscribers. If the list is linked with other peer lists, your request
X	will be forwarded to the appropriate server(s) also, except when the
X	request is live.
X
X	If you want statistics about all users to date (whether currently
X	subscribed or not) that have posted messages on the list, provide the
X	-all switch.
X
X	If a list is private, members only may issue this request.
*-*-END-of-help/statistics-*-*
echo x - help/subscribe
sed 's/^X//' >help/subscribe <<'*-*-END-of-help/subscribe-*-*'
XSyntax: subscribe <list> <your full name>
X        The only way to subscribe to a list. Any extraneous characters in
X	your name are removed.
X
X	An initial password will be assigned by the system and will be included
X	in the reply message; you should reset this password to something
X	memorable to you via a 'set' request. This password will be used
X	to provide you with subscriber privileges when telneting to this
X	host for live request processing. For more information on this
X	issue a 'help live' request.
*-*-END-of-help/subscribe-*-*
echo x - help/unsubscribe
sed 's/^X//' >help/unsubscribe <<'*-*-END-of-help/unsubscribe-*-*'
XSyntax: unsubscribe <list> (or: signoff <list>)
X        Remove yourself from a list.
*-*-END-of-help/unsubscribe-*-*
echo x - help/which
sed 's/^X//' >help/which <<'*-*-END-of-help/which-*-*'
XSyntax: which
X	Get a list of all mailing lists (served by this server) to
X	which you have subscribed.
*-*-END-of-help/which-*-*
echo x - archives/listserver/DIR
sed 's/^X//' >archives/listserver/DIR <<'*-*-END-of-archives/listserver/DIR-*-*'
Xinfo 1 52 /usr/server/archives/listserver Info about the master archive
Xsummary-of-requests 1 621 /usr/server/archives/listserver List of available requests\
X	This is a brief listing of all recognizable requests
Xexample.dat 3 27 27 27 /usr/server/archives/listserver/example.dat Example file
Xservice 1 119 /etc System's services file (non-existent)
*-*-END-of-archives/listserver/DIR-*-*
echo x - archives/listserver/INDEX
sed 's/^X//' >archives/listserver/INDEX <<'*-*-END-of-archives/listserver/INDEX-*-*'
XLISTSERVER /usr/server/archives/listserver
Xunix /usr/server/archives/unix
Xpub /usr/server/archives/pub
Xunix /usr/server/archives/pub/unix
Xprivate /usr/server/archives/pub/private johny-be-good
Xiul /usr/server/archives/iul
*-*-END-of-archives/listserver/INDEX-*-*
echo x - archives/listserver/summary-of-requests
sed 's/^X//' >archives/listserver/summary-of-requests <<'*-*-END-of-archives/listserver/summary-of-requests-*-*'
X
X		UNIX ListServer System, version 6.0
X
X			      March 1993
X
X
XRecognized requests are:
X
Xhelp [request]
Xset <list> [<option> <value>]
Xsubscribe <list> <your name>
Xunsubscribe <list> (or: signoff <list>)
Xrecipients <list> (or: review <list>)
Xinformation <list>
Xstatistics <list> {[subscriber email address(es)] | [-all]}
Xindex [archive | path-to-archive] [/password] [-all]
Xget <archive | path-to-archive> <file> [/password] [parts]
Xsearch <archive | path-to-archive> [/password] [-all] <pattern>
Xfax <fax-number> <archive | path-to-archive> <file> [/password] [parts]
Xrun <list> [password cmd [args]]
Xlists
Xwhich
Xrelease
X
*-*-END-of-archives/listserver/summary-of-requests-*-*
echo x - archives/listserver/info
sed 's/^X//' >archives/listserver/info <<'*-*-END-of-archives/listserver/info-*-*'
XPlace to put information for the LISTSERVER archive
*-*-END-of-archives/listserver/info-*-*
echo x - archives/listserver/example.dat/example.dat1
sed 's/^X//' >archives/listserver/example.dat/example.dat1 <<'*-*-END-of-archives/listserver/example.dat/example.dat1-*-*'
XPart 1 of file EXAMPLE.DAT
*-*-END-of-archives/listserver/example.dat/example.dat1-*-*
echo x - archives/listserver/example.dat/example.dat2
sed 's/^X//' >archives/listserver/example.dat/example.dat2 <<'*-*-END-of-archives/listserver/example.dat/example.dat2-*-*'
XPart 2 of file EXAMPLE.DAT
*-*-END-of-archives/listserver/example.dat/example.dat2-*-*
echo x - archives/listserver/example.dat/example.dat3
sed 's/^X//' >archives/listserver/example.dat/example.dat3 <<'*-*-END-of-archives/listserver/example.dat/example.dat3-*-*'
XPart 3 of file EXAMPLE.DAT
*-*-END-of-archives/listserver/example.dat/example.dat3-*-*
echo x - archives/pub/DIR
sed 's/^X//' >archives/pub/DIR <<'*-*-END-of-archives/pub/DIR-*-*'
Xinfo 1 46 /usr/server/archives/pub
*-*-END-of-archives/pub/DIR-*-*
echo x - archives/pub/INDEX
sed 's/^X//' >archives/pub/INDEX <<'*-*-END-of-archives/pub/INDEX-*-*'
XPUB /usr/server/archives/pub
Xunix /usr/server/archives/pub/unix
Xprivate /usr/server/archives/pub/private johny-be-good
*-*-END-of-archives/pub/INDEX-*-*
echo x - archives/pub/info
sed 's/^X//' >archives/pub/info <<'*-*-END-of-archives/pub/info-*-*'
XPlace to put information for the PUB archive.
*-*-END-of-archives/pub/info-*-*
echo x - archives/pub/unix/DIR
sed 's/^X//' >archives/pub/unix/DIR <<'*-*-END-of-archives/pub/unix/DIR-*-*'
Xinfo 1 44 /usr/server/archives/pub/unix
*-*-END-of-archives/pub/unix/DIR-*-*
echo x - archives/pub/unix/INDEX
sed 's/^X//' >archives/pub/unix/INDEX <<'*-*-END-of-archives/pub/unix/INDEX-*-*'
XUNIX /usr/server/archives/pub/unix
*-*-END-of-archives/pub/unix/INDEX-*-*
echo x - archives/pub/unix/info
sed 's/^X//' >archives/pub/unix/info <<'*-*-END-of-archives/pub/unix/info-*-*'
XPlace to put info for the PUB/UNIX archive.
*-*-END-of-archives/pub/unix/info-*-*
echo x - archives/pub/private/DIR
sed 's/^X//' >archives/pub/private/DIR <<'*-*-END-of-archives/pub/private/DIR-*-*'
Xinfo 1 47 /usr/server/archives/pub/private
*-*-END-of-archives/pub/private/DIR-*-*
echo x - archives/pub/private/INDEX
sed 's/^X//' >archives/pub/private/INDEX <<'*-*-END-of-archives/pub/private/INDEX-*-*'
XPRIVATE /usr/server/archives/pub/private johny-be-good
*-*-END-of-archives/pub/private/INDEX-*-*
echo x - archives/pub/private/info
sed 's/^X//' >archives/pub/private/info <<'*-*-END-of-archives/pub/private/info-*-*'
XPlace to put info for the PUB/PRIVATE archive.
*-*-END-of-archives/pub/private/info-*-*
echo x - archives/unix/DIR
sed 's/^X//' >archives/unix/DIR <<'*-*-END-of-archives/unix/DIR-*-*'
Xinfo 1 47 /usr/server/archives/unix
*-*-END-of-archives/unix/DIR-*-*
echo x - archives/unix/INDEX
sed 's/^X//' >archives/unix/INDEX <<'*-*-END-of-archives/unix/INDEX-*-*'
XUNIX /usr/server/archives/unix
*-*-END-of-archives/unix/INDEX-*-*
echo x - archives/unix/info
sed 's/^X//' >archives/unix/info <<'*-*-END-of-archives/unix/info-*-*'
XPlace to put information for the UNIX archive.
*-*-END-of-archives/unix/info-*-*
echo x - archives/iul/DIR
sed 's/^X//' >archives/iul/DIR <<'*-*-END-of-archives/iul/DIR-*-*'
Xiul.c 1 27525 /usr/server/archives/iul Interactive UNIX ListServer client
Xiul.h 1 2053 /usr/server/archives/iul Specific defs for iul.c
Xiulp.h 1 774 /usr/server/archives/iul Interactive UNIX ListServer Protocol
Xiul.1 1 5289 /usr/server/archives/iul man page
Xiul.nr 1 7308 /usr/server/archives/iul Preformatted man page
Xmakefile 1 2145 /usr/server/archives/iul
*-*-END-of-archives/iul/DIR-*-*
echo x - archives/iul/INDEX
sed 's/^X//' >archives/iul/INDEX <<'*-*-END-of-archives/iul/INDEX-*-*'
XIUL /usr/server/archives/iul
*-*-END-of-archives/iul/INDEX-*-*
echo x - archives/iul/iul.c
sed 's/^X//' >archives/iul/iul.c <<'*-*-END-of-archives/iul/iul.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                   INTERACTIVE UNIX-LISTSERVER CLIENT		     |
X  |									     |
X  |				  Version 1.0				     |
X  |									     |
X  | Author: Anastasios Kotsikonas					     |
X  | Date: March 29 1993							     |
X  ----------------------------------------------------------------------------
X
X  This program is to be used for connecting to an interactive UNIX ListServer
X  server (version 6.0 and up). The following files are required:
X
X  iul.c (this file)
X  iul.h
X  iulp.h (definition of the Interactive UNIX ListServer Protocol)
X  makefile
X
X  Usage: iul [-v] [-t timeout] [-b buffer-size] <host> [port]
X
X  To connect to a host you may specify its name or IP address. The default
X  port is 372; another port may be specified as an extra argument. To echo
X  server responses to client requests, use the -v flag.
X
X  The default time out for a server response is 180 seconds; to reset
X  use the -t flag. The default socket buffer size is 8K; to reset use
X  the -b flag (the argument specifies kilobytes).
X
X  This client is to be used as a model and better ones can certainly be
X  written.
X
X  SCO UNIX users should compile with -Dsco -I/usr/include/netinet. You may
X  need to grab and link with libbsd.a from /pub/sco-ports on
X  dribble.c-mols.siu.edu.
X
X  Hosts other than SUN, IBM, SGI, DEC, HP, Convex, Stardent, KPC, NeXT, SCO,
X  Apollo, Sequent, Data General should compile with -Dunknown_port.
X
X  Refer to the man page for more information on how to use this program.
X*/
X
X#define TCP_IP	/* Otherwise you may not use this client */
X
X#ifdef TCP_IP
X#include <stdio.h>
X#include <string.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X# include <unistd.h>
X# else
X# include <libc.h>
X# endif
X#endif
X#include <sys/types.h>
X#if defined (stardent) || defined (stellar) || defined (titan)
X# include <rpc/types.h>
X#endif
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X# ifdef unknown_port
Xextern int errno;
X# endif
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <signal.h>
X#include <sys/file.h>
X#include <sys/ioctl.h>
X#if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X#include <sys/stropts.h>
X#endif
X#if (defined (sco) || defined (M_XENIX) || defined (M_UNIX)) && \
X  defined (HAVE_SELECT_H)
X#include <sys/times.h>
X#else
X#include <sys/time.h>
X#endif
X#ifdef HAVE_SELECT_H
X#include <sys/select.h>
X#endif
X#ifdef HAVE_ULIMIT_H
X#include <ulimit.h>
X#endif
X#include "iul.h"
X
X#ifndef UL_GDESLIM
X#define UL_GDESLIM	4
X#endif
X#ifndef FD_SET          /* for 4.2BSD */
X#define FD_SETSIZE      (sizeof(fd_set) * 8)
X#define FD_SET(n, p)    (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
X#define FD_CLR(n, p)    (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
X#define FD_ISSET(n, p)  (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
X#define FD_ZERO(p)      memset ((char *)(p), '\0', sizeof(*(p)))
X#endif
X
X#ifndef __NeXT__
Xextern	long int atoi (char *);
X#else
Xextern	int atoi (const char *);
X#endif
Xextern  int optind;
X#if !defined (hpux) && !defined (__hpux)
Xextern  int getopt (int, char **, char *);
X#endif
X
Xint     main (int, char **);
Xint	server_response (int);
Xint     build_tcp_connection (char *, int);
Xint	sighandle (int);
Xint	urg_data (int);
Xlong int read_from_fd (int, long int, FILE *);
Xlong int write_to_fd (int, char *, long int);
Xint	open_file (char *, int, int);
Xint 	check_server_response (int, char *);
Xint	check_for_redirection (char *, char *);
XFILE    *check_for_pipe (char *);
X
Xlong int nbytes, sock_fd, buffsiz = BUFFSIZ;
Xint  to_file, prompt_len, verbose, check_sio, response_timeout = TIMEOUT,
X     fd = -1, broken_pipe = 0, sigint = 1;
Xchar args [256];
XFILE *pipefp = NULL;
X#endif
X
X/*
X  Main function. Connect to server, issue requests and receive replies.
X
X  Returns: 0 on success, 1 on command line option error, or an IULP
X	   command if the transaction was terminated normally.
X*/
X
Xmain (int argc, char **argv)
X{
X#ifndef TCP_IP
X  printf ("%s not functional.\n", argv[0]);
X#else
X  char *options = "vt:b:";
X  char *buf, *tmp, infile [256], wbuf [256], version [80];
X  int cmd, i, port, timeout, sprompt_len, c, rfd, bytes_alloced = 0;
X  struct stat stat_buf;
X  extern char *optarg;
X 
X  port = PORT;
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch ((char) c) {
X      case 'v': verbose = 1; break;
X      case 't':
X	if ((response_timeout = atoi (optarg)) < 1) {
X	  printf ("-t %d\nBe real.\n", response_timeout);
X	  return 1;
X	}
X	break;
X      case 'b':
X	if ((buffsiz = atol (optarg) * 1024) < 1024) {
X	  printf ("-b %ld\nBe real.\n", buffsiz);
X	  return 1;
X	}
X	break;
X      case '?':
X      default:
X	fprintf (stderr, "Usage: %s [-v] [-t timeout] [-b buffer-size] \
X<host> [port]\n", argv[0]);
X	return 1;
X    }
X
X  if (optind == argc) {
X    fprintf (stderr, "Usage: %s [-v] [-t timeout] [-b buffer-size] \
X<host> [port]\n", argv[0]);
X    return 1;
X  }
X  else if (optind + 1 < argc)
X    port = atoi (argv [optind + 1]);
X# ifdef SIGIO
X  signal (SIGIO, (void (*)()) urg_data);
X# endif
X# ifdef SIGURG
X  signal (SIGURG, (void (*)()) urg_data);
X# endif
X  if ((sock_fd = build_tcp_connection (argv[optind], port)) < 0)
X    return SYS_ERROR;
X  if ((cmd = server_response (sock_fd)) != CONNECT) {
X    PRINTF (cmd, "Handshake failed\n");
X    printf ("Not an interactive UNIX ListServer.\n");
X    return cmd;
X  }
X
X  for (i = SIGHUP; i <= SIGPIPE; i++)
X    signal (i, (void (*)()) sighandle);
X
X  if (check_server_response (cmd, args)) goto abort;
X  timeout = nbytes;
X  sscanf (args, "%s %d %d\n", version, &prompt_len, &sprompt_len);
X
X  if ((cmd = server_response (sock_fd)) < 0) goto abort; /* Get greeting */
X  check_server_response (cmd, args);
X  if (cmd != CONN_CLOSED && cmd != SERVER_BUSY && cmd != PEER_UNAVAIL &&
X      cmd != CONN_ABORTED && cmd != SYS_ERROR && cmd != CONN_TIMEOUT)
X    printf ("\nHit ^\\ (control-backslash) to abort at any time.\n");
X  if (read_from_fd (sock_fd, nbytes, NULL) < 0) goto abort;
X  if (cmd == CONN_CLOSED || cmd == SERVER_BUSY || cmd == PEER_UNAVAIL ||
X      cmd == CONN_ABORTED || cmd == SYS_ERROR || cmd == CONN_TIMEOUT)
X    goto abort;
X  bytes_alloced = 256;
X  if ((buf = (char *) malloc (bytes_alloced * sizeof (char))) == NULL) {
X    printf ("FATAL: malloc() failed\n");
X    goto abort;
X  }
X  check_sio = 1;
X  fgets (buf, bytes_alloced - 1, stdin);		/* Put email address */
X  if (strlen (buf) == 0)
X    printf ("\n"),
X    strcpy (buf, "\n");
X  check_sio = 0;
X  if (write_to_fd (sock_fd, buf, strlen (buf)) < 0) goto abort;
X  if ((cmd = server_response (sock_fd)) < 0) /* See if "Password: " follows */
X    goto abort;
X  if (check_server_response (cmd, args)) goto abort;
X  if (cmd == PASSWORD_REQUIRED) {
X    RESET (buf);
X    check_sio = 1;
X    fgets (buf, 255, stdin);
X    if (strlen (buf) == 0)
X      printf ("\n"),
X      strcpy (buf, "\n");
X    check_sio = 0;
X    if (write_to_fd (sock_fd, buf, strlen (buf)) < 0)
X      goto abort;
X  }
X  if (cmd == PEER_UNAVAIL) {
X    read_from_fd (sock_fd, nbytes, NULL);
X    goto abort;
X  }
X
X  sigint = 0;
X  RESET (buf);
X  while (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT &&
X	 cmd != PEER_UNAVAIL) {
X    if ((cmd = server_response (sock_fd)) < 0) break;
X    if (check_server_response (cmd, args)) break;
X    if (cmd == TEST_FILE_PERMISSIONS) continue;
X    if (cmd != CONN_ABORTED && cmd != CONN_TIMEOUT)
X      if (read_from_fd (sock_fd, nbytes, (cmd != MESSAGE ? pipefp : NULL)) < 0)
X	break;
X    broken_pipe = 0;
X    if (cmd == MESSAGE) continue;
X    if (cmd != CONN_CLOSED && cmd != CONN_ABORTED && cmd != CONN_TIMEOUT &&		cmd != PEER_UNAVAIL) {
X      check_sio = 1;
X      if (pipefp)
X	strcpy (buf, "\n");
X      if (buf [0] == EOS) {
X        RESET (infile);
X        fgets (buf, bytes_alloced - 1, stdin);
X        if (strlen (buf) == 0)    /* EOF */
X	  printf ("\n"),
X	  strcpy (buf, "\n");
X        if ((rfd = check_for_redirection (buf, infile)) < 0)
X	  strcpy (buf, "\n");
X        if (rfd >= 0 && infile [0] != EOS) { /* Input redirection */
X	  fstat (rfd, &stat_buf);
X	  if (!(stat_buf.st_mode & S_IFREG)) {
X	    printf ("%s: Not a regular file\n", infile);
X	    strcpy (buf, "\n");
X	    goto skip;
X	  }
X	  if (stat_buf.st_size + strlen (buf) + 2 > bytes_alloced) {
X	    bytes_alloced = stat_buf.st_size + strlen (buf) + 2;
X	    if ((buf = (char *) realloc (buf, bytes_alloced * sizeof (char))) ==
X	        NULL) {
X	      printf ("FATAL: realloc() failed\n");
X	      break;
X	    }
X	  }
X	  i = strlen (buf);
X	  if (read (rfd, &buf [strlen (buf)], stat_buf.st_size) <
X	      stat_buf.st_size) {
X	    printf ("FATAL: Failed to read all of the input file\n");
X	    break;
X	  }
X	  buf [i + stat_buf.st_size] = EOS;
X	  if (buf [i + stat_buf.st_size - 1] != '\n')
X	    strcat (&buf [i + stat_buf.st_size], "\n");
X	  close (rfd);
X        }
X      }
X      skip:
X      strncpy (wbuf, buf, 254);
X      tmp = strchr (wbuf, '\n');
X      if (!tmp)
X	wbuf [254] = '\n',
X	wbuf [255] = EOS;
X      else
X	*(tmp + 1) = EOS;
X      if (pipefp)
X	pclose (pipefp);
X      check_sio = 0;
X      pipefp = check_for_pipe (wbuf);
X      if (write_to_fd (sock_fd, wbuf, strlen (wbuf)) < 0) break;
X      sprintf (buf, "%s", strchr (buf, '\n') + 1);
X    }
X  }
X
X  abort:
X  if (pipefp)
X    pclose (pipefp);
X  close (sock_fd);
X  printf ("Connection closed.\n");
X  if (buf)
X    free ((char *) buf);
X  if (cmd == CONN_CLOSED) 
X    return 0;
X  return cmd;
X#endif
X}
X
X#ifdef TCP_IP
X/*
X  Get server response. Store in 'nbytes' the number of bytes in the actual
X  message that follows, and in 'args' the (optional) file name.
X
X  Returns: the server reply code, CONN_TIMEOUT, CONN_ABORTED, or -1 on 
X  protocol errors.
X*/
X
Xint server_response (int sock_fd)
X{
X  char buf [256];
X  int cmd = -1, i, bytes_to_read = 4, bytes_read = 0, err, l = sizeof (int);
X  long int time_started;
X
X  time_started = time (0);
X  memset (buf, EOS, 256);
X  while (bytes_to_read) {
X    errno = 0;
X    /* Get server command */
X    bytes_read = read (sock_fd, &buf [4 - bytes_to_read], bytes_to_read);
X    if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	errno == ECONNREFUSED) {
X      nbytes = 0;
X      return CONN_ABORTED;
X    }
X    if (!getsockopt (sock_fd, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l)){
X      if (err || buf [4 - bytes_to_read] == EOF) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X    }
X    else
X      perror ("getsockopt() error");
X    if (errno || bytes_read < 1)
X      if ((time (0) - time_started) > response_timeout) {
X	nbytes = 0;
X	return CONN_TIMEOUT;
X      }
X    if (bytes_read > 0)
X      bytes_to_read -= bytes_read;
X  }
X  buf[3] = EOS;
X  cmd = atoi (buf);
X  i = 0;
X  do {		/* Get # of bytes in actual message */
X    again1:
X    if (read (sock_fd, &buf[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	  errno == ECONNREFUSED) {
X	nbytes = 0;
X	return CONN_ABORTED;
X      }
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X	if ((time (0) - time_started) > response_timeout) {
X	  nbytes = 0;
X	  return CONN_TIMEOUT;
X	}
X	goto again1;
X      }
X      perror ("Protocol error in control string");
X      return -1;
X    }
X    i++;
X  } while (buf[i - 1] != ' ');
X
X  buf[i - 1] = EOS;
X  nbytes = atoi (buf);
X  RESET (args);
X  i = 0;
X  do {		/* Get filename (optional) */
X    again2:
X    if (read (sock_fd, &args[i], 1) < 1) {
X      if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X          errno == ECONNREFUSED)
X        return CONN_ABORTED;
X      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
X        if ((time (0) - time_started) > response_timeout) {
X          nbytes = 0;
X          return CONN_TIMEOUT;
X        }
X        goto again2;
X      }
X      perror ("Protocol error in control string");
X      return -1;
X    }
X    ++i;
X  } while (args[i - 1] != '\n');
X  args[i - 1]= EOS;
X  return cmd;
X}
X
X/*
X  Establish connection with unix-listserver. The socket is marked as
X  non-blocking.
X
X  Returns: the socket file descriptor, or -1 on error.
X*/
X
Xint build_tcp_connection (char *host, int port)
X{
X  int sock_fd, sendbuf = buffsiz, recvbuf = buffsiz, value = 1, nfds, naddr = 0;
X  struct sockaddr_in sin;
X  struct hostent *hostentry;
X  struct timeval timeout;
X  fd_set readfds, writefds;
X 
X  timeout.tv_sec = timeout.tv_usec = 0;
X  if (!(hostentry = gethostbyname (host))) { /* Host name failed; try IP */
X    sin.sin_addr.s_addr = inet_addr (host);
X    if (! (hostentry = gethostbyaddr ((char *) &sin.sin_addr,
X				      sizeof (struct in_addr),
X				      AF_INET))) {
X      printf ("%s: No such host.\n", host);
X      return -1;
X    }
X  }
X
X  do {	/* Check all addresses */
X    if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X      perror ("Could not create socket");
X      return -1;
X    }
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		    sizeof (sendbuf)) < 0)
X      perror ("WARNING: Could not set send-buffer size: setsockopt() error");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		    sizeof (recvbuf)) < 0)
X      perror ("WARNING: Could not set receive-buffer size: setsockopt() error");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value,
X		    sizeof (value)) < 0)
X      perror ("WARNING: Cannot toggle keep-alive connections: setsockopt() error");
X
X# ifdef h_addr
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr_list[naddr++],
X	    hostentry->h_length);
X# else
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr,
X	    hostentry->h_length);
X# endif
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons (port);
X    memset (sin.sin_zero, EOS, sizeof (sin.sin_zero));
X    printf ("Trying %s ...\n", inet_ntoa (sin.sin_addr));
X    if (connect (sock_fd, (struct sockaddr *) &sin, 
X		 sizeof (struct sockaddr_in)) < 0) {
X      if (errno != EINPROGRESS) {
X# ifdef h_addr
X	if (!hostentry->h_addr_list[naddr]) {
X# endif	
X	  perror ("Could not connect to host");
X	  close (sock_fd);
X	  return -1;
X# ifdef h_addr
X	}
X	else {
X	  close (sock_fd);
X	  continue;
X	}
X# endif
X      }
X      FD_ZERO (&readfds);
X      FD_ZERO (&writefds);
X      do {
X	FD_SET (sock_fd, &readfds);
X	FD_SET (sock_fd, &writefds);
X	errno = 0;
X# if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X  defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X# else
X	nfds = ulimit (UL_GDESLIM);
X# endif
X	value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds, NULL,
X			&timeout);
X      } while (value == -1 && errno == EINTR);
X
X      if (value < 0) {
X	perror ("select() error");
X	close (sock_fd);
X	return -1;
X      }
X      break;	/* Successful connection */
X    }
X    else
X      break;
X  } while (007);
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | O_NDELAY)) < 0) {
X    perror ("Could not set non-blocking I/O");
X    close (sock_fd);
X    return -1;
X  }
X# if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X  if (ioctl (sock_fd, I_SETSIG, S_INPUT) < 0) {
X    perror ("Cannot set SIGIO");
X    close (sock_fd);
X    return -1;
X  }
X# else
X#  ifdef F_SETOWN
X  if (fcntl (sock_fd, F_SETOWN, getpid()) < 0) {
X    perror ("Cannot assign socket to process group");
X    close (sock_fd);
X    return -1;
X  }
X#  elif defined (SIOCSPGRP)
X  value = -getpid();
X  if (ioctl (sock_fd, SIOCSPGRP, (char *) &value) < 0) {
X    perror ("Cannot assign socket to process group");
X    close (sock_fd);
X    return -1;
X  }
X#  else
X  perror ("Cannot assign socket to process group");
X  close (sock_fd);
X  return -1;
X#  endif
X#  ifdef FASYNC
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | FASYNC)) < 0) {
X    perror ("Cannot set asynchronous I/O for socket");
X    close (sock_fd);
X    return -1;
X  }
X#  elif defined (FIOASYNC)
X  value = 1;
X  if (ioctl (sock_fd, FIOASYNC, (char *) &value) < 0) {
X    perror ("Cannot set asynchronous I/O for socket");
X    close (sock_fd);
X    return -1;
X  }
X#  else
X  perror ("Cannot set asynchronous I/O for socket");
X  close (sock_fd);
X  return -1;
X#  endif
X# endif
X  return sock_fd;
X}
X
X/*
X  Handle signals; the client sends an '__abort__' command and exits; '__abort__'
X  causes the other end to shut down.
X*/
X
Xint sighandle (int sig)
X{
X  if (sig == SIGHUP) {
X    signal (sig, (void (*)()) sighandle);
X    return;
X  }
X  else if (sig == SIGPIPE) {
X    if (!broken_pipe)
X      printf ("Broken pipe\n");
X    broken_pipe = 1;
X    signal (sig, (void (*)()) sighandle);
X    return;
X  }
X  else if (sig == SIGINT) {
X    if (!sigint) {
X      signal (sig, (void (*)()) sighandle);
X      return;
X    }
X  }
X  check_sio = 0;
X  write_to_fd (sock_fd, "__abort__\n", 10);
X  close (sock_fd);
X  if (pipefp)
X    pclose (pipefp);
X  printf ("Connection closed.\n");
X  exit (sig);
X}
X
X/*
X  Urgent data from server; read it and exit. Urgent data is received
X  when a connection is timed out or when the server shuts down.
X*/
X
Xint urg_data (int sig)
X{
X  int cmd;
X
X  if (!check_sio) {
X    signal (sig, (void (*)()) urg_data);
X    return;
X  }
X  signal (sig, SIG_IGN);
X  if ((cmd = server_response (sock_fd)) >= 0)
X    if (cmd == CONN_ABORTED || cmd == CONN_TIMEOUT || cmd == CONN_CLOSED)
X      check_server_response (cmd, args),
X      read_from_fd (sock_fd, nbytes, NULL);
X  shutdown (sock_fd, 2);
X  kill (0, SIGHUP);
X  if (pipefp)
X    pclose (pipefp);
X  printf ("Connection closed.\n");
X  exit (cmd);
X}
X
X/*
X  Read from a socket and either echo to tty or save to a file.
X
X  Returns: the actual number of bytes read on succes, or the negative of
X  that number (or -1) on error.
X*/
X
Xlong int read_from_fd (int rfd, long int bytes_to_read, FILE *pipefp)
X{
X  long int bytes_read, total_bytes = 0, time_started;
X  int to_pipe = 1;
X  char *buf;
X
X  time_started = time (0);
X  if (bytes_to_read <= 0)
X    return 0;
X  if ((buf = (char *) malloc (buffsiz * sizeof (char))) == NULL) {
X    printf ("malloc() failed while attempting to allocate %ld bytes\n",
X	     buffsiz);
X    return -1;
X  }
X  errno = 0;
X  while ((bytes_read = read (rfd, buf, MIN (bytes_to_read, buffsiz))) <
X	 bytes_to_read) {
X    if (bytes_read < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN &&
X	errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      switch (errno) {
X	case EBADF: sprintf (error, "Bad file number"); break;
X	case EFAULT: sprintf (error, "Bad address"); break;
X	case EFBIG: sprintf (error, "File limit reached"); break;
X	case EINVAL: sprintf (error, "Negative seek pointer"); break;
X	case EIO: sprintf (error, "I/O error"); break;
X	case ENOSPC: sprintf (error, "No space left on device"); break;
X	case ENXIO: sprintf (error, "No such device or address"); break;
X	case ERANGE: sprintf (error, "Bytes to read (%d) out of \
Xrange", bytes_to_read); break;
X	default: sprintf (error, "Error number %d", errno);
X      }
X      fprintf (stderr, "%s\n", error);
X      free ((char *) buf);
X      return -1;
X    }
X    else if (!pipefp && (time (0) - time_started) > response_timeout) {
X      printf ("Server response timeout.\n");
X      free ((char *) buf);
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    if (bytes_read > 0) {
X      total_bytes += bytes_read;
X      bytes_to_read -= bytes_read;
X      if (to_file) {
X	if (bytes_to_read < prompt_len)
X	  write_to_fd (fileno (stdout),
X		       &buf[bytes_read - prompt_len + bytes_to_read],
X		       prompt_len - bytes_to_read),
X	  to_file = 0;
X	if (write_to_fd (fd, buf,
X			 (bytes_to_read < prompt_len ? 
X			  bytes_read - prompt_len + bytes_to_read :
X			  bytes_read)) < 0) {
X	  free ((char *) buf);
X	  return (total_bytes > 0 ? -total_bytes : -1);
X	}
X      }
X      else if (pipefp) {
X	if (to_pipe && !broken_pipe) {
X	  write_to_fd (fileno (pipefp), buf,
X		       (bytes_to_read < prompt_len ? 
X			bytes_read - prompt_len + bytes_to_read :
X			bytes_read));
X	  if (errno) {
X	    if (!broken_pipe && errno == EPIPE)
X	      printf ("Broken pipe\n");
X	    broken_pipe = 1;
X	    if (errno != EPIPE && errno != EIO
X#ifdef ERESTART
X		&& errno != ERESTART
X#endif
X		)
X	      printf ("errno %d\n", errno);
X	  }
X	}
X	if (bytes_to_read < prompt_len)
X	  to_pipe = 0;
X      }
X      else
X	write_to_fd (fileno (stdout), buf, bytes_read);
X    }
X    errno = 0;
X  }
X  if (bytes_read > 0) {
X    total_bytes += bytes_read;
X    if (to_file && bytes_read > prompt_len) {
X      write_to_fd (fileno (stdout), &buf [bytes_read - prompt_len], prompt_len);
X      if (write_to_fd (fd, buf, 
X		       (bytes_read > prompt_len ?
X			bytes_read - prompt_len : bytes_read)) < 0) {
X	free ((char *) buf);
X	return (total_bytes > 0 ? -total_bytes : -1);
X      }
X    }
X    else if (pipefp) {
X      if (!broken_pipe) {
X	write_to_fd (fileno (pipefp), buf,
X		     (bytes_read > prompt_len ? bytes_read - prompt_len
X		      : 0));
X	if (errno) {
X	  if (!broken_pipe && errno == EPIPE)
X	    printf ("Broken pipe\n");
X	  if (errno != EPIPE && errno != EIO
X#ifdef ERESTART
X	      && errno != ERESTART
X#endif
X	      )
X	    printf ("errno %d\n", errno);
X	}
X      }
X    }
X    else
X      write_to_fd (fileno (stdout), buf, bytes_read);
X  }
X  if (fd >= 0)
X    close (fd),
X    fd = -1;
X  to_file = 0;
X  free ((char *) buf);
X  fflush (stdout);
X  if (pipefp)
X    fflush (pipefp);
X  return total_bytes;
X}
X
X/*
X  Write to a file descriptor (socket, or regular file).
X
X  Returns: the actual number of bytes written on succes, or the negative of
X  that number (or -1) on error.
X*/
X
Xlong int write_to_fd (int wfd, char *buf, long int bytes_to_write)
X{
X  long int bytes_written, total_bytes = 0, bytes_requested = bytes_to_write;
X
X  if (bytes_to_write == 0)
X    return 0;
X  errno = 0;
X  while ((bytes_written = write (wfd, buf, bytes_to_write)) < bytes_to_write) {
X    if (bytes_written < 0 && errno && errno != EWOULDBLOCK && errno != EAGAIN
X	&& errno != EINTR
X#ifdef ERESTART
X	&& errno != ERESTART
X#endif
X	) {
X      char error [256];
X      switch (errno) {
X	case EBADF: sprintf (error, "Bad file number"); break;
X	case EFAULT: sprintf (error, "Bad address"); break;
X	case EFBIG: sprintf (error, "File limit reached"); break;
X	case EINVAL: sprintf (error, "Negative seek pointer"); break;
X	case EIO: sprintf (error, "I/O error"); break;
X	case ENOSPC: sprintf (error, "No space left on device"); break;
X	case ENXIO: sprintf (error, "No such device or address"); break;
X	case ERANGE: sprintf (error, "Bytes to write (%d) out of \
Xrange", bytes_to_write); break;
X	case EPIPE: sprintf (error, "Server disappeared"); break;
X	default: sprintf (error, "Error number %d", errno);
X      }
X      fprintf (stderr, "%s\n", error);
X      if (bytes_written > 0)
X	total_bytes += bytes_written;
X      return (total_bytes > 0 ? -total_bytes : -1);
X    }
X    if (bytes_written > 0)
X      bytes_to_write -= bytes_written,
X      total_bytes += bytes_written,
X      buf += bytes_written;
X    errno = 0;
X  }
X  return bytes_requested;
X}
X
X/*
X  Open a file for writing or appending. If 'test' is set, make sure
X  the file is writeable, notify the server and close the file; else prepare
X  for the transfer. When the server is inquiring about a file's write
X  permissions it sends zero bytes as the length of the message.
X
X  Returns: 0 on succes, -1 otherwise.
X*/
X
Xint open_file (char *file, int mode, int test)
X{
X  char msg [4];
X
X  if ((fd = open (file, O_CREAT | mode, 0600)) < 0)
X    to_file = 0,
X    printf ("%s: Permission denied.\n", args),
X    nbytes = prompt_len,
X    sprintf (msg, "%d", PERMISSION_DENIED);
X  else {
X    if (nbytes > 0)
X      to_file = 1,
X      printf ("Transferring %ld bytes to file %s...\n", nbytes - prompt_len,
X	      file);
X    else
X      close (fd),
X      sprintf (msg, "%d", OK);
X  }
X  if (test)
X    if (write_to_fd (sock_fd, msg, strlen (msg)) < 0)
X      return -1;
X  return 0;
X}
X
X/*
X  Identify the server's response and take appropriate action.
X
X  Returns: 0 on success, -1 otherwise.
X*/
X
Xint check_server_response (int cmd, char *file)
X{
X    switch (cmd) {
X    case OK:
X    case CONNECT:
X    case SYNTAX_ERROR:
X    case INVALID_REQ:
X    case PEER_UNAVAIL:
X    case BAD_ARCHIVE:
X    case RESTRICTED_REQ:
X    case NOT_OWNER:
X    case SYS_ERROR:
X    case SERVER_BUSY:
X    case PASSWORD_REQUIRED:
X    case PERMISSION_DENIED:
X    case MESSAGE:
X    case CONTINUED:
X    case MORE_INPUT_REQUIRED:
X    case CONN_ABORTED:
X    case CONN_TIMEOUT:
X    case CONN_CLOSED: GENERAL_RESPONSES (cmd); break;
X    case TEST_FILE_PERMISSIONS: PRINTF (TEST_FILE_PERMISSIONS,
X	 "Testing file permissions\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 1)) return -1;
X	 break;
X    case WRITE_TO_FILE_ASC: PRINTF (WRITE_TO_FILE_ASC,
X	 "Writing to file (ASCII)\n");
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case WRITE_TO_FILE_BIN: PRINTF (WRITE_TO_FILE_BIN,
X	 "Writing to file (BIN)\n");
X	 if (open_file (file, O_WRONLY | O_TRUNC, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_ASC: PRINTF (APPEND_TO_FILE_ASC,
X	 "Appending to file (ASCII)\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    case APPEND_TO_FILE_BIN: PRINTF (APPEND_TO_FILE_BIN,
X	 "Appending to file (BIN)\n");
X	 if (open_file (file, O_WRONLY | O_APPEND, 0)) return -1;
X	 break;
X    default: printf ("Protocol error: %d\n", cmd); return -1;
X  }
X  return 0;
X}
X
X/*
X  Check for input redirection, and if so check for for permissions too.
X
X  Returns: the opened file descriptor on succes, -1 on error.
X*/
X
Xint check_for_redirection (char *buf, char *infile)
X{
X  char *r, *s = strrchr (buf, EOS);
X  int fd = 0, nquote = 0;
X
X  while (s >= buf) {
X    if (!nquote) {
X      if (*s == '\'' && (s == buf || *(s - 1) != '\\')) /* Count single quotes */
X	nquote = 1;
X      else if (*s == '"' && (s == buf || *(s - 1) != '\\')) /* Count double quotes */
X	nquote = 2;
X      else if (*s == '<')
X	if (s != buf && *(s - 1) == '\\')
X	  sprintf (s - 1, "%s", s), /* Remove \ */
X	  --s;
X	else {
X	  RESET (infile);
X	  sscanf (s + 1, "%s", infile);
X	  if (infile [0] == EOS) {
X	    printf ("Invalid null input redirect\n");
X	    return -1;
X	  }
X	  if ((fd = open (infile, O_RDONLY)) < 0) {
X	    printf ("%s: No such file or inadequate permissions\n", infile);
X	    return -1;
X	  }
X	  r = s + 1; /* Remove < and file name */
X	  while (*r == ' ' || *r == '\t') ++r;
X	  while (*r != ' ' && *r != '\t' && *r != '\n') ++r;
X	  sprintf (s, "%s", r);
X	}
X    }
X    else if ((nquote == 1 && *s == '\'' &&
X	      (s != buf ? *(s - 1) != '\\' : 1)) ||
X	     (nquote == 2 && *s == '"' && 
X	      (s != buf ? *(s - 1) != '\\' : 1)))
X      nquote = 0;
X    --s;
X  }
X  if (nquote) {
X    printf ("Mismatched quotes\n");
X    strcpy (buf, "\n");
X    if (fd > 0)
X      close (fd);
X    return -1;
X  }
X  return fd;
X}
X
X/*
X  Check for UNIX pipe.
X
X  Returns: the open file pointer, or NULL.
X*/
X
XFILE *check_for_pipe (char *buf)
X{
X  char cmd [1024], *s = buf;
X  int nquote = 0;
X  FILE *pipe = NULL;
X
X  RESET (cmd);
X  while (*s != EOS) {
X    if (!nquote) {
X      if (*s == '\'' && (s == buf || *(s - 1) != '\\')) /* Count single quotes */
X	nquote = 1;
X      else if (*s == '"' && (s == buf || *(s - 1) != '\\')) /* Count double quotes */
X	nquote = 2;
X      else if (*s == '|' && cmd[0] == EOS)
X	if (s != buf && *(s - 1) == '\\')
X	  sprintf (s - 1, "%s", s), /* Remove \ */
X	  --s;
X	else {
X	  sscanf (s + 1, "%s", cmd);
X	  if (cmd [0] == EOS) {
X	    printf ("Invalid null pipe\n");
X	    strcpy (buf, "\n");
X	    return NULL;
X	  }
X	  strcpy (cmd, s + 1);
X	  strcpy (s, "\n");
X	  pipe = popen (cmd, "w");
X	}
X    }
X    else if ((nquote == 1 && *s == '\'' && *(s - 1) != '\\') ||
X	     (nquote == 2 && *s == '"' && *(s - 1) != '\\'))
X      nquote = 0;
X    ++s;
X  }
X  if (nquote)
X    printf ("Mismatched quotes\n"),
X    strcpy (buf, "\n");
X  return pipe;
X}
X#endif
*-*-END-of-archives/iul/iul.c-*-*
echo x - archives/iul/iul.h
sed 's/^X//' >archives/iul/iul.h <<'*-*-END-of-archives/iul/iul.h-*-*'
X/*
X  Below are the #define's pertinent to iul.c
X*/
X
X#include "iulp.h"
X
X#define PORT            	372	/* As assigned by IANA */
X#define MAX_HOPS		5	/* ... from IUL server to IUL server */
X#define EOS             	'\0'
X#define TIMEOUT         	180 /* How long to wait for server response */
X#ifndef BUFFSIZ
X# define BUFFSIZ         	8192
X#endif
X#ifndef RESET
X# define RESET(v)        	(v[0] = EOS)
X#endif
X#define PRINTF(code, str)	if (verbose) printf ("%d %s", code, str),\
X				fflush (stdout)
X#ifndef MIN
X# define MIN(a, b)		((a) < (b) ? (a) : (b))
X#endif
X
X#define GENERAL_RESPONSES(cmd)\
X{\
X  switch (cmd) {\
X    case OK: PRINTF (OK, "OK\n"); break;\
X    case CONNECT: PRINTF (CONNECT, "Connected\n"); break;\
X    case SYNTAX_ERROR: PRINTF (SYNTAX_ERROR, "Syntax error in request\n");\
X	 break;\
X    case INVALID_REQ: PRINTF (INVALID_REQ, "Invalid request\n"); break;\
X    case PEER_UNAVAIL: PRINTF (PEER_UNAVAIL, "Peer unavailable\n"); break;\
X    case BAD_ARCHIVE: PRINTF (BAD_ARCHIVE, "Bad archive\n"); break;\
X    case RESTRICTED_REQ: PRINTF (RESTRICTED_REQ,\
X	 "Restriction in force for request\n"); break;\
X    case NOT_OWNER: PRINTF (NOT_OWNER, "Invalid onwer\n"); break;\
X    case SYS_ERROR: PRINTF (SYS_ERROR, "System error\n"); break;\
X    case MESSAGE: PRINTF (MESSAGE, "Message\n"); break;\
X    case PERMISSION_DENIED: PRINTF (PERMISSION_DENIED, "Permission denied\n");\
X	 break;\
X    case CONN_CLOSED: PRINTF (CONN_CLOSED, "Closing connection\n"); break;\
X    case CONN_ABORTED: PRINTF (CONN_ABORTED, "Connection aborted\n");\
X	 return -1; break;\
X    case CONN_TIMEOUT: PRINTF (CONN_TIMEOUT, "Connection timed out\n");\
X	 return -1; break;\
X    case SERVER_BUSY: PRINTF (SERVER_BUSY, "Server busy\n"); break;\
X    case PASSWORD_REQUIRED: PRINTF (PASSWORD_REQUIRED, "Password required\n");\
X         if (read_from_fd (sock_fd, nbytes, NULL) < 0) return -1; break;\
X    case CONTINUED: PRINTF (CONTINUED, "[Command incomplete:]"); break;\
X    case MORE_INPUT_REQUIRED: PRINTF (MORE_INPUT_REQUIRED,\
X	 "[Command incomplete:]"); break;\
X  }\
X}
*-*-END-of-archives/iul/iul.h-*-*
echo x - archives/iul/iulp.h
sed 's/^X//' >archives/iul/iulp.h <<'*-*-END-of-archives/iul/iulp.h-*-*'
X/*
X  Define the Interactive UNIX ListServer Protocol.
X*/
X
X#define OK                      100
X#define MORE_INPUT_REQUIRED	101
X#define CONTINUED		102
X#define PASSWORD_REQUIRED	103
X#define CONNECT			105
X#define MESSAGE			106
X#define WRITE_TO_FILE_ASC	110
X#define WRITE_TO_FILE_BIN	120
X#define APPEND_TO_FILE_ASC	130
X#define APPEND_TO_FILE_BIN	140
X#define TEST_FILE_PERMISSIONS	154
X#define SYNTAX_ERROR            200
X#define INVALID_REQ             300
X#define PEER_UNAVAIL            400
X#define BAD_ARCHIVE             500
X#define RESTRICTED_REQ          600
X#define NOT_OWNER               700
X#define SYS_ERROR               800
X#define PERMISSION_DENIED	860
X#define SERVER_BUSY             870
X#define CONN_TIMEOUT		880
X#define CONN_ABORTED		890
X#define CONN_CLOSED		900
*-*-END-of-archives/iul/iulp.h-*-*
echo x - archives/iul/makefile
sed 's/^X//' >archives/iul/makefile <<'*-*-END-of-archives/iul/makefile-*-*'
X#
X# Makefile for iul: Interactive UNIX ListServer client
X#
X# iul is written in ANSI C. If you do not have an ANSI C compiler
X# you will need to obtain and use 'unproto' (it is available from
X# cs.bu.edu via anonymous ftp in the directory pub/unproto).
X#
X# To use unproto select the proper SRC and COMP symbols from below,
X# comment out the default ones and type 'make'.
X#
X# On some system that do not use BSD-style sockets and signals (SIGIO)
X# need to link with a BSD library that defines them, although this is not
X# critical. See the man page for iul(1) for more details.
X#
X# Compilation suggestions:
X# SGI: cc -ansi -D_POSIX_SOURCE, or cc -D__STDC__ -D_POSIX_SOURCE
X# SUN: Use /usr/lang/acc when available
X# NeXT: Compile with -D__NeXT__ if the compiler does not define this symbol,
X#  and -I/usr/include/bsd -I/usr/include/bsd/sys
X# HP-UX: cc -Aa -D_HPUX_SOURCE
X# IBM: xlc -D_ALL_SOURCE
X# SYSVR4 Systems: link with -lnsl and -lsocket or -lsockdns if you are using
X#   a name server
X# SCO: Compile with -Dsco -I/usr/include/netinet
X# Apollo: Do not compile with -A ansi; if you need to, also compile with
X#   -Dapollo
X# Sequent: gcc -fwritable-strings
X# Data General: gcc -fwritable-strings
X# Stardent/KPC Titan: cc (do not use -43)
X# OSF: cc -std1
X#
X# Platforms that have the <sys/select.h> and/or <ulimit.h> header files
X# should use -DHAVE_SELECT_H -DHAVE_ULIMIT_H when compiling. SCO ports should
X# compile with -Dsco.
X
X
X# define your compiler:
XCC		= cc
X
X# define special symbols (system, compiler, etc):
XDEFINES		= -DHAVE_SELECT_H -DHAVE_ULIMIT_H
X
X# define the optimization level
XOPTIMIZATION	= -g
X
X# define linking options and libraries
XLDFLAGS		= $(OPTIMIZATION)
XLIBS		=
X
X# unproto specific definitions -- use either COMP symbol:
X#SRC		= /tmp/$<
X#COMP		= cat $< | unproto > $(SRC); $(CC)
X#COMP		= /lib/cpp $(DEFINES) $< | unproto > $(SRC); $(CC)
X
X# default definitions:
XSRC		= $<
XCOMP		= $(CC)
X
X# nothing to change below:
XCFLAGS		= -c $(OPTIMIZATION) $(DEFINES)
XLD		= $(CC)
X
Xall:	iul
X
Xclean:
X	rm -f iul iul.o
X
Xiul:	iul.o
X	$(LD) $(LDFLAGS) $@.o -o $@ $(LIBS) -lm
X	@rm -f /tmp/$@.c
X
Xiul.o:	iul.c iul.h iulp.h
X	$(COMP) $(CFLAGS) $(SRC)
X
*-*-END-of-archives/iul/makefile-*-*
echo x - archives/iul/iul.1
sed 's/^X//' >archives/iul/iul.1 <<'*-*-END-of-archives/iul/iul.1-*-*'
X.\" UNIX ListServer System
X.\" Copyright (c) 1991-93, Anastasios Kotsikonas
X.\"
X.TH iul 1 "IUL 1.0"
X.SH NAME
X\fBiul\fP \- Interactive UNIX ListServer client
X.SH SYNOPSIS
X\fBiul\fP [\fB-v\fP] [\fB-t timeout\fP] [\fB-b buffer-size\fP] \fBhost\fP [\fBport\fP]
X.SH DESCRIPTION
X\fIiul\fP is a client that connects to the specified \fIhost\fP's
XInteractive UNIX ListServer port (372) for a live session -- i.e. for
Xprocessing requests live. Another \fIport\fP
Xmay be specified if necessary. The \fIhost\fP may be a host name, or an 
XIP address.
X.PP
XUpon connection establishment, the protocol attempts to set the user's
Xprivileges during the session by requesting an email address and a password.
XThese are used to determine whether the user is to be granted system,
Xowner, subscriber or casual user privileges. If the user provides
Xhis owner email address and his list's password, he will be granted
Xowner privileges. If he provides an email address that he uses for subscription
Xto a list, along with the password he uses for that list, he will be granted
Xsubscriber privileges. The system provides a brief listing of all valid
Xrequests the user may issue during the session. If no email address is
Xprovided, or no matches are found, the user is restricted to a few
Xrequests.
X.PP
XRequests for remote lists are serviced by attempting to connect
Xto the IUL server(s) (if any) of the system(s) that handle those lists.
X.PP
XThe IUL server recognizes the following special requests:
X.TP
Xquit/exit
XEnd the session.
X.TP
X?/privileges
XGet a brief listing of all valid requests.
X.TP
Xtimeleft
XPrints the remaining time in seconds.
X.TP
X&<new-line>
XInput continues on the next line.
X.TP
Xbinary
XSwitch to binary mode when transferring files.
X.TP
Xascii
XSwitch to ASCII mode when transferring files.
X.TP
X< filename
XInput is taken from the specified file. Each line will be interpreted as
Xa separate request, unless the file is specified in conjunction with a
X\fIput\fP request. If the '<' is to be used literally it
Xmust be escaped with '\\' or enclosed in quotes.
X.TP
X> filename
XRedirect the reply to the request to the specified file. Error messages
X(such as rejections due to invalid requests, etc.) are not redirected.
XWhen a file is downloaded via a \fIget\fP request, this will override the
Xfile name that will be saved under. If the '>' is to be used literally it
Xmust be escaped with '\\', or enclosed in quotes.
X.TP
X>> filename
XSame as above, but the reply is appended to the specified file.
X.TP
X| prog [args]
XThe output of the request is piped to \fIprog\fP; this is similar to a UNIX
Xpipe. \fIprog\fP may be any valid UNIX command, including other pipes, file
Xredirections, etc. Since '<' has higher precedence in this context, you
Xshould escape any '<' characters intended to be used by the pipe, otherwise
Xthe system will assume you are feeding it batched requests. Quotes may also
Xbe used to protect these characters.
X.SH OPTIONS
XThe following options are recognized:
X.TP
X-v
XTurn verbose mode on; the server reply codes are echoed along with
Xpredetermined messages.
X.TP
X-t timeout
XThe default time out for a server response is 180 seconds; to reset
Xuse the -t flag. This timeout should not be confused with the
Xremaining time of a connection.
X.TP
X-b buffer-size
XThe default socket buffer size is 8K; to reset use the -b flag
X(the argument specifies kilobytes).
X
X.SH USER\ PRIVILEGES
XCasual users may only issue help, information, recipients and statistics
Xfor nonprivate lists, lists, index, get, search and release requests.
XSubscriber privileges also include the set, run, unsubscribe and which requests.
XOwners may, in addition, issue all of their administrative requests.
X.PP
XIssue a 'help live' request when you first connect to an IUL server.
X.SH RESTRICTIONS
XThe connection duration is limited to a server-imposed limit.
XAfter that, a connection may be broken by the server as necessary.
XA connection will not be broken during a transfer.
X.SH EXAMPLES
X.nf
Xrequest> put ermis ermis1 subscribers </usr/server/backup/lists/ERMIS/subs >out
X.sp
Xrequest> get listserver example.dat 1 3 >> out
X.sp
Xrequest> get listserver example.dat
X.sp
Xrequest> index iul > IUL
X.sp
Xrequest> < /tmp/batched.requests
X.sp
Xrequest> index | more
X.sp
Xrequest> lists | cut -d ' ' -f1,2 | more
X.sp
Xrequest> search iul "\\>\\> out"
X.sp
X.fi
XIn this last example, we escape '>' to protect it from being interpreted
Xas a regular expression separator, and enclose the whole pattern in quotes
Xto protect the '>>' from being interpreted as the append operator. The same
Xrequest could have been written as:
X.nf
X.sp
Xrequest> search iul \\\\>\\\\> out
X.fi
X.SH FILES
X.TP
Xiul.c, iul.h
XSource code for the client. See iul.c for compile options.
X.TP
Xiulp.h
XDefinition of the Interactive UNIX ListServer Protocol.
X.TP
Xmakefile
Xmakefile to build \fIiul\fP. \fIiul\fP is written with BSD-style signal
Xhandling, therefore on some hosts (like IBM AIX) you will have to link with
XBSD versions of signal(2), socket(3N), and fcntl(2). Platforms that
Xhave the <sys/select.h> and <ulimit.h> header files should compile
Xwith -DHAVE_SELECT_H and/or -DHAVE_ULIMIT_H. SCO ports should compile with
X-Dsco. Always link with libraries that provide DNS support (resolver linked
Xin).
X.SH BUGS
XPlease report any bugs or enhancements to tasos@cs.bu.edu
*-*-END-of-archives/iul/iul.1-*-*
echo x - archives/iul/iul.nr
sed 's/^X//' >archives/iul/iul.nr <<'*-*-END-of-archives/iul/iul.nr-*-*'
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
XNNNNAAAAMMMMEEEE
X     iiiiuuuullll - Interactive UNIX ListServer client
X
XSSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X     iiiiuuuullll [----vvvv] [----tttt ttttiiiimmmmeeeeoooouuuutttt] [----bbbb bbbbuuuuffffffffeeeerrrr----ssssiiiizzzzeeee] hhhhoooosssstttt [ppppoooorrrrtttt]
X
XDDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X     _i_u_l is a  client  that  connects  to  the  specified  _h_o_s_t's
X     Interactive UNIX ListServer port (372) for a live session --
X     i.e. for processing  requests  live.  Another  _p_o_r_t  may  be
X     specified  if  necessary. The _h_o_s_t may be a host name, or an
X     IP address.
X
X     Upon connection establishment, the protocol attempts to  set
X     the  user's  privileges  during the session by requesting an
X     email address and a password.  These are used  to  determine
X     whether  the user is to be granted system, owner, subscriber
X     or casual user privileges. If the user  provides  his  owner
X     email  address  and  his list's password, he will be granted
X     owner privileges. If he provides an email  address  that  he
X     uses  for subscription to a list, along with the password he
X     uses  for  that  list,  he  will   be   granted   subscriber
X     privileges. The system provides a brief listing of all valid
X     requests the user may issue during the session. If no  email
X     address  is  provided,  or no matches are found, the user is
X     restricted to a few requests.
X
X     Requests for remote lists are serviced by attempting to con-
X     nect  to  the  IUL  server(s) (if any) of the system(s) that
X     handle those lists.
X
X     The IUL server recognizes the following special requests:
X
X     quit/exit
X          End the session.
X
X     ?/privileges
X          Get a brief listing of all valid requests.
X
X     timeleft
X          Prints the remaining time in seconds.
X
X     &<new-line>
X          Input continues on the next line.
X
X     binary
X          Switch to binary mode when transferring files.
X
X     ascii
X          Switch to ASCII mode when transferring files.
X
X     < filename
X
X
X
XAnastasios Kotsikonas                                               1
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X          Input is taken from the specified file. Each line  will
X          be  interpreted  as a separate request, unless the file
X          is specified in conjunction with a _p_u_t request. If  the
X          '<' is to be used literally it must be escaped with '\'
X          or enclosed in quotes.
X
X     > filename
X          Redirect the reply to  the  request  to  the  specified
X          file. Error messages (such as rejections due to invalid
X          requests, etc.) are not redirected.   When  a  file  is
X          downloaded  via  a  _g_e_t request, this will override the
X          file name that will be saved under. If the '>' is to be
X          used literally it must be escaped with '\', or enclosed
X          in quotes.
X
X     >> filename
X          Same as above, but the reply is appended to the  speci-
X          fied file.
X
X     | prog [args]
X          The output of the request is piped  to  _p_r_o_g;  this  is
X          similar to a UNIX pipe. _p_r_o_g may be any valid UNIX com-
X          mand, including other pipes,  file  redirections,  etc.
X          Since  '<'  has  higher precedence in this context, you
X          should escape any '<' characters intended to be used by
X          the  pipe,  otherwise  the  system  will assume you are
X          feeding it batched requests. Quotes may also be used to
X          protect these characters.
X
XOOOOPPPPTTTTIIIIOOOONNNNSSSS
X     The following options are recognized:
X
X     -v   Turn verbose mode on; the server reply codes are echoed
X          along with predetermined messages.
X
X     -t timeout
X          The default time out  for  a  server  response  is  180
X          seconds;  to reset use the -t flag. This timeout should
X          not be confused with the remaining time  of  a  connec-
X          tion.
X
X     -b buffer-size
X          The default socket buffer size is 8K; to reset use  the
X          -b flag (the argument specifies kilobytes).
X
X
XUUUUSSSSEEEERRRR PPPPRRRRIIIIVVVVIIIILLLLEEEEGGGGEEEESSSS
X     Casual users may only issue  help,  information,  recipients
X     and  statistics  for  nonprivate  lists,  lists, index, get,
X     search and release  requests.   Subscriber  privileges  also
X     include  the set, run, unsubscribe and which requests.  Own-
X     ers may, in addition,  issue  all  of  their  administrative
X
X
X
XAnastasios Kotsikonas                                               2
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X     requests.
X
X     Issue a 'help live' request when you first connect to an IUL
X     server.
X
XRRRREEEESSSSTTTTRRRRIIIICCCCTTTTIIIIOOOONNNNSSSS
X     The connection  duration  is  limited  to  a  server-imposed
X     limit.  After that, a connection may be broken by the server
X     as necessary.  A connection will  not  be  broken  during  a
X     transfer.
X
XEEEEXXXXAAAAMMMMPPPPLLLLEEEESSSS
X     request> put ermis ermis1 subscribers </usr/server/backup/lists/ERMIS/subs >out
X
X     request> get listserver example.dat 1 3 >> out
X
X     request> get listserver example.dat
X
X     request> index iul > IUL
X
X     request> < /tmp/batched.requests
X
X     request> index | more
X
X     request> lists | cut -d ' ' -f1,2 | more
X
X     request> search iul "\>\> out"
X
X     In this last example, we escape '>' to protect it from being
X     interpreted  as  a regular expression separator, and enclose
X     the whole pattern in quotes to protect the '>>'  from  being
X     interpreted  as  the append operator. The same request could
X     have been written as:
X
X     request> search iul \\>\\> out
X
XFFFFIIIILLLLEEEESSSS
X     iul.c, iul.h
X          Source code for  the  client.  See  iul.c  for  compile
X          options.
X
X     iulp.h
X          Definition of the Interactive UNIX ListServer Protocol.
X
X     makefile
X          makefile to build _i_u_l. _i_u_l is  written  with  BSD-style
X          signal handling, therefore on some hosts (like IBM AIX)
X          you will have to link with BSD versions  of  signal(2),
X          socket(3N),  and  fcntl(2).  Platforms  that  have  the
X          <sys/select.h> and <ulimit.h> header files should  com-
X          pile  with  -DHAVE_SELECT_H and/or -DHAVE_ULIMIT_H. SCO
X          ports should  compile  with  -Dsco.  Always  link  with
X
X
X
XAnastasios Kotsikonas                                               3
X
X
X
X
X
X
Xiul(1)                   USER COMMANDS                     iul(1)
X
X
X
X          libraries  that  provide  DNS  support (resolver linked
X          in).
X
XBBBBUUUUGGGGSSSS
X     Please report any bugs or enhancements to tasos@cs.bu.edu
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XAnastasios Kotsikonas                                               4
X
X
X
*-*-END-of-archives/iul/iul.nr-*-*
echo x - gateway/gateway.c
sed 's/^X//' >gateway/gateway.c <<'*-*-END-of-gateway/gateway.c-*-*'
X/*
X  ----------------------------------------------------------------------------
X  |                      LIVE UNIX LISTSERVER GATEWAY			     |
X  |                                                                          |
X  |                               Version 1.0                                |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                           (tasos@cs.bu.edu)                              |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the files	     |
X  | gateway.c and gateway.h.						     |
X  | This software comes with no warranties and cannot be sold for profit.    |
X  | The AGREEMENT and COPYRIGHT notices should be included in all source     |
X  | files when distributing this software.                                   |
X  | COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas		     |
X  | Use, duplication or disclosure by the Federal Government is subject to   |
X  | the restrictions set forth in FAR 52.227-19(c), Commercial Computer      |
X  | Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1) |
X  | (ii).                                                                    |
X  ----------------------------------------------------------------------------
X
X  Forward live UNIX ListServer connections to the specified host connecting
X  to the specified port. It requires TCP/IP.
X
X  Usage: gateway <host> <port> > log-file 2&>1 &
X
X  Compilation options:
X    -DDEBUG: Turn traffic tracing on.
X    -DWAIT_NEEDS_UNION: if the argument to wait() should be a union
X    -DIUL_PORT=port: if you do not want to use /etc/services
X    -DMAX_CONNECTIONS=number: to reset the default (5)
X
X  SCO UNIX systems should compile with -Dsco -I/usr/include/netinet. Sequent
X  users should compile with -Dsequent, and NeXT users with -D__NeXT__, if
X  these symbols are not automatically defined.
X
X  Hosts other than SUN, IBM, SGI, DEC, HP, Convex, Stardent, KPC, NeXT, SCO,
X  Apollo, Sequent, Data General should compile with -Dunknown_port.
X
X*/
X
X#include <stdio.h>
X#ifndef unknown_port
X# ifndef __NeXT__
X#  include <unistd.h>
X# else
X#  include <libc.h>
X# endif
X#endif
X#include <sys/types.h>
X#if defined (stardent) || defined (stellar) || defined (titan)
X# include <rpc/types.h>
X#endif
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <ctype.h>
X#include <string.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <sys/socket.h>
X#include <sys/time.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <sys/file.h>
X#include <sys/ioctl.h>
X#if defined (sco) || defined (M_XENIX) || defined (M_UNIX)
X# include <sys/stropts.h>
X#endif
X#ifdef HAVE_SELECT_H
X# include <sys/select.h>
X#endif
X#ifdef HAVE_ULIMIT_H
X# include <ulimit.h>
X#endif
X#include <sys/wait.h>
X#ifdef HAVE_POLL
X# include <stropts.h>
X# include <poll.h>
X#endif
X#include "gateway.h"
X
X#ifndef __NeXT__
Xextern int atoi ();
X#endif
X
Xvoid   main (int, char **, char **);
Xvoid   sighandle (int);
Xvoid   close_connection (int);
Xint    create_connection (void);
Xint    build_tcp_connection (char *, int);
Xvoid   process_live_request (int, char *, int, int);
XBOOLEAN host_listed (char *, char *);
Xchar   *upcase (char *);
Xint    write_to_fd (int, char *, long int);
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  char reply [256], *s;
X  int connid = 0, pid, _nforks, l, mask;
X  struct sockaddr_in client;
X  struct hostent *hp;
X
X  if (argc < 3)
X    printf ("Usage: %s <host> <port>\n", argv[0]),
X    exit (3);
X#ifdef SIGCLD
X  signal (SIGCLD, (void (*)()) sighandle);
X#else
X  signal (SIGCHLD, (void (*)()) sighandle);
X#endif
X  signal (SIGHUP, SIG_IGN);
X  signal (SIGINT, (void (*)()) sighandle);
X  signal (SIGTERM, (void (*)()) sighandle);
X  signal (SIGSEGV, (void (*)()) sighandle);
X  signal (SIGILL, (void (*)()) sighandle);
X  signal (SIGQUIT, (void (*)()) sighandle);
X  signal (SIGBUS, (void (*)()) sighandle);
X  signal (SIGTERM, (void (*)()) sighandle);
X
X
X  if (getuid () & geteuid ())
X    printf ("WARNING: gateway not started with root privileges\n");
X
X  if ((sock_fd = create_connection ()) < 0)
X    fprintf (stderr, "main(): Cannot create connection\n"),
X    exit (13);
X  while (007) { /* Listen for ever */
X
X    l = sizeof (client);
X    if ((msg_sock = accept (sock_fd, (struct sockaddr *) &client,
X			    (int *) &l)) < 0) {
X      if (errno != EINTR)
X	perror ("\nmain(): Cannot accept()"),
X	exit (13);
X      continue;
X    }
X    else { /* New client; see if we can handle it */
X      hp = gethostbyaddr ((char *) &client.sin_addr,
X			  sizeof (struct in_addr), client.sin_family);
X      printf ("Connection request from %s: ",
Xupcase ((s = (hp ? hp->h_name : (char *) inet_ntoa (client.sin_addr)))));
X      if (!stat (PRIVILEGED_HOSTSF, &stat_buf) && stat_buf.st_size > 0)
X	if (!host_listed (PRIVILEGED_HOSTSF, s)) {
X	  CANNOT_CONNECT ("not a privileged host\n", CONN_ABORTED,
X			  "Not a privileged host.\n");
X	  continue;
X	}
X      if (host_listed (UNWANTED_HOSTSF, s)) {
X	CANNOT_CONNECT ("unwanted host\n", CONN_ABORTED,
X			"Not a privileged host.\n");
X	continue;
X      }
X
X      if (nforks < 0 || nforks > MAX_CONNECTIONS)
X	fprintf (stderr, "\nmain(): Internal error: nforks=%d\n", nforks),
X	exit (16);
X      if (nforks + 1 > MAX_CONNECTIONS) { /* Too many connections */
X	CANNOT_CONNECT ("refused\n", SERVER_BUSY, SERVER_BUSY_);
X	continue;
X      }
X      printf ("connected (client #%d)\n", connid);
X      /* Parent increments the number of times it has forked */
X      _nforks = nforks;
X      clients [nforks].time = time (0); /* Save time child spawned */
X      clients [nforks].connid = connid++;
X      strcpy (clients [nforks++].name,
X	      (hp ? hp->h_name : (char *) inet_ntoa (client.sin_addr)));
X#if defined (bsd)
X# ifdef SIGCLD
X      mask = sigblock (sigmask (SIGCLD));
X# else
X      mask = sigblock (sigmask (SIGCHLD));
X# endif
X#elif defined (svr4) || defined (svr3)
X# ifdef SIGCLD
X      sighold (SIGCLD);
X# else
X      sighold (SIGCHLD);
X# endif
X#endif
X      if ((pid = fork ()) < 0) {	/* fork */
X	CANNOT_CONNECT ("cannot fork\n", SYS_ERROR, "Remote system error.\n");
X#if defined (bsd)
X	sigsetmask (mask);
X#elif defined (svr3) || defined (svr4)
X# ifdef SIGCLD
X	sigrelse (SIGCLD);
X# else
X	sigrelse (SIGCHLD);
X# endif
X#endif
X	continue;
X      }
X      else if (pid == 0) {		/* Child process */
X	close (sock_fd);	/* Close unused parent socket */
X#ifdef SIGCLD
X	signal (SIGCLD, SIG_DFL);
X#else
X	signal (SIGCHLD, SIG_DFL);
X#endif
X	signal (SIGHUP, (void (*)()) close_connection);
X	signal (SIGPIPE, (void (*)()) close_connection);
X	signal (SIGINT, SIG_IGN);
X	signal (SIGTERM, (void (*)()) close_connection);
X	signal (SIGSEGV, (void (*)()) close_connection);
X	signal (SIGILL, (void (*)()) close_connection);
X	signal (SIGQUIT, (void (*)()) close_connection);
X	signal (SIGBUS, (void (*)()) close_connection);
X	signal (SIGALRM, (void (*)()) close_connection);
X	process_live_request (msg_sock, argv[1], atoi (argv[2]), connid - 1);
X	close (msg_sock);
X	_exit (0);
X      }
X      else
X	clients [_nforks].pid = pid,
X#if defined (bsd)
X	sigsetmask (mask),
X#elif defined (svr3) || defined (svr4)
X# ifdef SIGCLD
X	sigrelse (SIGCLD),
X# else
X	sigrelse (SIGCHLD),
X# endif
X#endif
X	close (msg_sock);
X    }
X  }
X}
X
X/*
X  Handle signals, especially SIGCLD.
X*/
X
Xvoid sighandle (int sig)
X{
X#ifdef WAIT_NEEDS_UNION
X  union wait status;
X#else
X  int status = 1;
X#endif
X  int pid, i, j, _nforks, mask, exstatus;
X
X#ifdef SIGCLD
X  if (sig != SIGCLD)
X    signal (sig, SIG_IGN);
X  if (sig == SIGCLD) {
X#else
X  if (sig != SIGCHLD)
X    signal (sig, SIG_IGN);
X  if (sig == SIGCHLD) {
X#endif
X#if defined (bsd)
X# ifdef SIGCLD
X    mask = sigblock (sigmask (SIGCLD));
X# else
X    mask = sigblock (sigmask (SIGCHLD));
X# endif
X#elif defined (svr4) || defined (svr3)
X# ifdef SIGCLD
X    sighold (SIGCLD);
X# else
X    sighold (SIGCHLD);
X# endif
X#endif
X    pid = wait (&status);   /* Get child's pid */
X    for (i = 0; i < nforks; i++) {
X      if (clients [i].pid == pid) { /* Remove dead child's pid */
X        printf ("Connection closed with %s (client #%d)\n",
X	        clients [i].name, clients [i].connid);
X	for (j = i; j < nforks - 1; j++)
X	  memcpy ((char *) &clients [j], (char *) &clients [j + 1], 
X		  sizeof (CLIENT));
X	break;
X      }
X    }
X    if (i == nforks)
X      printf ("Connection closed with last client\n");
X    --nforks;
X    if (WIFSIGNALED (status))
X      printf ("\nsighandle(): Child died abnormally: signal %d\n",
X	      WTERMSIG (status));
X    else if (WIFEXITED (status)) { /* Abnormal child exit status ? */
X#if defined (bsd)
X      sigsetmask (mask);
X#elif defined (svr3) || defined (svr4)
X# ifdef SIGCLD
X      sigrelse (SIGCLD);
X# else
X      sigrelse (SIGCHLD);
X# endif
X#endif
X      signal (sig, (void (*)()) sighandle);
X      if ((exstatus = WEXITSTATUS (status)) == SIGSEGV || exstatus == SIGBUS ||
X	  exstatus == SIGQUIT || exstatus == SIGILL)
X	printf ("\nsighandle(): Child received signal %d\n", exstatus);
X      else if (exstatus == SIGPIPE) {
X	printf ("\nsighandle(): Child experienced socket error\n");
X	return;
X      }
X      else if (exstatus == SIGTERM)
X	printf ("\nsighandle(): Child initiates shutdown\n");
X      else if (exstatus == SIGHUP || exstatus == SIGINT || 
X	       exstatus == SIGALRM || exstatus == 0)
X	return;
X    }
X  }
X#if defined (bsd)
X  sigsetmask (mask);
X#elif defined (svr3) || defined (svr4)
X# ifdef SIGCLD
X  sigrelse (SIGCLD);
X  signal (SIGCLD, SIG_DFL);
X# else
X  sigrelse (SIGCHLD);
X  signal (SIGCHLD, SIG_DFL);
X# endif
X#endif
X  for (i = 0; i < nforks; i++) { /* Kill any "live" children */
X    printf ("Connection closed with %s (client #%d)\n",
X	    clients [i].name, clients [i].connid);
X    kill (clients [i].pid, SIGTERM);
X  }
X  exit (0);
X}
X
X/*
X  Child has been notified that it should commit suicide, so it does after
X  notifying the client.
X*/
X
Xvoid close_connection (int sig)
X{
X  char s [256];
X
X  signal (sig, SIG_IGN);
X  if (sig == SIGALRM)
X    sprintf (s, "%d %d \n%s", CONN_TIMEOUT, strlen (CONN_TIMED_OUT),
X	     CONN_TIMED_OUT);
X  else if (sig == SIGTERM)
X    sprintf (s, "%d %d \n%s", CONN_ABORTED, strlen (SERVER_SHUTS_DOWN),
X	     SERVER_SHUTS_DOWN);
X  else
X    sprintf (s, "%d %d \n%s", CONN_ABORTED, strlen (REMOTE_SYS_ERROR),
X	     REMOTE_SYS_ERROR);
X  write_to_fd (msg_sock, s, strlen (s));
X  write_to_fd (server_sock, "__abort__\n", 10);
X  close (msg_sock);
X  close (server_sock);
X  _exit (sig);
X}
X
X/*
X  Create a socket and bind it to the local address and to an available port.
X  Return the socket file descriptor, or -1 on error.
X*/
X
Xint create_connection ()
X{
X  struct sockaddr_in server;
X  struct servent *service;
X  struct hostent *hostentry;
X  int sock, timeout = 0, sendbuf = BUFFSIZ, recvbuf = BUFFSIZ, val = 1;
X
X#ifndef IUL_PORT
X  if (! (service = getservbyname (SERVICE, NULL))) {
X    fprintf (stderr, "\ncreate_connection(): Service unavailable\n");
X    return -1;
X  }
X#endif
X  if (! (hostentry = gethostbyname ("localhost"))) {
X    fprintf (stderr, "\ncreate_connection(): No such host\n");
X    return -1;
X  }
X  if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X    perror ("\ncreate_connection(): Could not create socket");
X    return -1;
X  }
X  if (setsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		  sizeof (sendbuf)) < 0)
X    perror ("\ncreate_connection(): Could not set send-buffer size: \
Xsetsockopt() error");
X  if (setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		  sizeof (recvbuf)) < 0)
X    perror ("\ncreate_connection(): Could not set receive-buffer size: \
Xsetsockopt() error");
X  if (setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &val,
X      sizeof (val)) < 0)
X    perror ("\ncreate_connection(): WARNING: Cannot toggle keep-alive \
Xconnections: setsockopt() error");
X  val = 1;
X  if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &val,
X      sizeof (val)) < 0)
X    perror ("\ncreate_connection(): WARNING: Cannot toggle reuse of local \
Xaddress: setsockopt() error");
X
X  server.sin_family = AF_INET;            /* Internet protocol */
X  server.sin_addr.s_addr = INADDR_ANY;
X#ifndef IUL_PORT
X  server.sin_port = service->s_port;
X#else
X  server.sin_port = htons (IUL_PORT);
X#endif
X
X  while (bind (sock, (struct sockaddr *) &server, sizeof (server)) < 0 &&
X	 errno == EADDRINUSE && timeout < 180) { /* For perm port keep trying */
X    ++timeout;
X    errno = 0;
X    sleep (1);
X  }
X  if (timeout >= 180) {
X    fprintf (stderr, "\ncreate_connection(): Could not bind", TRUE);
X    return -1;
X  }
X  listen (sock, 5);
X  return sock;
X}
X
X/*
X  Establish connection with unix-listserver. The socket is marked as
X  non-blocking.
X
X  Returns: the socket file descriptor, or -1 on error.
X*/
X
Xint build_tcp_connection (char *host, int port)
X{
X  int sock_fd, sendbuf = BUFFSIZ, recvbuf = BUFFSIZ, value = 1, nfds, naddr = 0;
X  struct sockaddr_in sin;
X  struct hostent *hostentry;
X  struct timeval timeout;
X  fd_set readfds, writefds;
X 
X  timeout.tv_sec = timeout.tv_usec = 0;
X  if (!(hostentry = gethostbyname (host))) { /* Host name failed; try IP */
X    sin.sin_addr.s_addr = inet_addr (host);
X    if (! (hostentry = gethostbyaddr ((char *) &sin.sin_addr,
X				      sizeof (struct in_addr),
X				      AF_INET))) {
X      fprintf (stderr, "%s: No such host.\n", host);
X      return -1;
X    }
X  }
X
X  do {  /* Check all addresses */
X    if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
X      perror ("Could not create socket");
X      return -1;
X    }
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, (char *) &sendbuf,
X		    sizeof (sendbuf)) < 0)
X      perror ("WARNING: Could not set send-buffer size");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, (char *) &recvbuf,
X		    sizeof (recvbuf)) < 0)
X      perror ("WARNING: Could not set receive-buffer size");
X    if (setsockopt (sock_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value,
X		    sizeof (value)) < 0)
X      perror ("WARNING: Cannot toggle keep-alive connections");
X
X#ifdef h_addr
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr_list[naddr++],
X	    hostentry->h_length);
X#else
X    memcpy ((char *) &sin.sin_addr.s_addr,
X	    (char *) hostentry->h_addr,
X	    hostentry->h_length);
X#endif
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons (port);
X    memset (sin.sin_zero, EOS, sizeof (sin.sin_zero));
X    if (connect (sock_fd, (struct sockaddr *) &sin,
X		 sizeof (struct sockaddr_in)) < 0) {
X      if (errno != EINPROGRESS) {
X#ifdef h_addr
X	if (!hostentry->h_addr_list[naddr]) {
X#endif
X	  perror ("Could not connect to host");
X	  return -1;
X#ifdef h_addr
X	}
X	else {
X	  close (sock_fd);
X	  continue;
X	}
X#endif
X      }
X      FD_ZERO (&readfds);
X      FD_ZERO (&writefds);
X      do {
X	FD_SET (sock_fd, &readfds);
X	FD_SET (sock_fd, &writefds);
X	errno = 0;
X#if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X#else
X	nfds = ulimit (UL_GDESLIM);
X#endif
X	value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds, NULL,
X			&timeout);
X      } while (value == -1 && errno == EINTR);
X
X      if (value < 0) {
X	perror ("select() error");
X	return -1;
X      }
X      break;    /* Successful connection */
X    }
X    else
X      break;
X  } while (007);
X  if (fcntl (sock_fd, F_SETFL, (fcntl (sock_fd, F_GETFL, 0) | FNDELAY)) < 0) {
X    perror ("Could not set non-blocking I/O");
X    return -1;
X  }
X  return sock_fd;
X}
X
X
X/*
X  Process all live requests coming in from the tcp connection.
X
X  PLEASE DO NOT MODIFY THIS ROUTINE AS YOU MAY BREAK THE PROTOCOL AND CREATE
X  HAVOC WITH PEER INTERACTIVE UNIX LISTSERVERS. A LOT OF UNDOCUMENTED
X  ASSUMPTIONS ARE MADE. YOU SHOULD CONTACT ME ABOUT ANY CHANGES YOU WISH TO
X  MAKE.
X*/
X
Xvoid process_live_request (int msg_sock, char *host, int port, int connid)
X{
X  int err, l, value, times = 2, nfds, cookie;
X  long int bytes_read, total_bytes = 0;
X  char buf[BUFFSIZ];
X  struct timeval timeout;
X  fd_set readfds, writefds, exceptfds;
X  BOOLEAN die;
X#ifdef HAVE_POLL
X  struct pollfd pollfds[2];
X#endif
X
X  timeout.tv_sec = timeout.tv_usec = 1;
X  if (fcntl (msg_sock, F_SETFL, (fcntl (msg_sock, F_GETFL, 0) | FNDELAY)) < 0) {
X    fprintf (stderr, "Client #%d: ", connid);
X    perror ("Could not set non-blocking I/O");
X    return;
X  }
X  if ((server_sock = build_tcp_connection (host, port)) < 0) {
X    CANNOT_CONNECT ("", CONN_ABORTED, "Gateway cannot connect to server.\n");
X    return;
X  }
X  while (007) {
X    FD_ZERO (&readfds);
X    FD_ZERO (&writefds);
X    FD_ZERO (&exceptfds);
X    do {
X      FD_SET (server_sock, &readfds);
X      FD_SET (msg_sock, &readfds);
X      FD_SET (msg_sock, &writefds);
X      FD_SET (server_sock, &writefds);
X      FD_SET (server_sock, &exceptfds);
X      FD_SET (msg_sock, &exceptfds);
X      errno = 0;
X#if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X#else
X	nfds = ulimit (UL_GDESLIM);
X#endif
X      value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds,
X		      &exceptfds, &timeout);
X
X      if (value < 0) {
X	fprintf (stderr, "Client #%d: ", connid);
X	perror ("select() error");
X	close (server_sock);
X	return;
X      }
X#ifdef HAVE_POLL
X      if (value == 0) {
X	pollfds[0].fd = msg_sock;
X	pollfds[1].fd = server_sock;
X	poll ((struct pollfd *) pollfds, 2, 1);
X	if ((pollfds[0].revents & POLLERR) || (pollfds[0].revents & POLLHUP) ||
X	    (pollfds[0].revents & POLLNVAL) || 
X	    (pollfds[1].revents & POLLERR) || (pollfds[1].revents & POLLHUP) ||
X	    (pollfds[1].revents & POLLNVAL)) {
X	  fprintf (stderr, "Client #d: Clients gone.\n", connid);
X	  close (server_sock);
X	  return;
X	}
X      }
X#endif
X      err = 1;
X      l = sizeof (int);
X      getsockopt (msg_sock, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l);
X      if (err) {
X	fprintf (stderr, "Client #%d: client socket error\n", connid);
X	close (server_sock);
X	return;
X      }
X      err = 1;
X      l = sizeof (int);
X      getsockopt (server_sock, SOL_SOCKET, SO_ERROR, (char *) &err, (char *) &l);
X      if (err) {
X	fprintf (stderr, "Client #%d: server socket error\n", connid);
X	close (server_sock);
X	return;
X      }
X    } while (value == -1 && errno == EINTR);
X
X    if (FD_ISSET (msg_sock, &readfds)) {
X      while ((bytes_read = read (msg_sock, buf, BUFFSIZ)) > 0) {
X#ifdef DEBUG
X	printf ("Client #%d: request: ", connid);
X	fflush (stdout);
X	write_to_fd (fileno (stdout), buf, bytes_read);
X#endif
X	if (write_to_fd (server_sock, buf, bytes_read) < 0) {
X	  fprintf (stderr, "Client #%d: write-to-server socket failed\n",
X		   connid);
X	  return;
X	}
X	FD_ZERO (&readfds); /* Work around a stupid AIX problem */
X	FD_ZERO (&writefds);
X#if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X#else
X	nfds = ulimit (UL_GDESLIM);
X#endif
X	do {
X	  FD_SET (msg_sock, &readfds);
X	  FD_SET (server_sock, &writefds);
X	  errno = 0;
X	  value = select ((nfds > 0 ? nfds : 20), &readfds, &writefds,
X			  NULL, &timeout);
X	  if (value < 0) {
X	    fprintf (stderr, "Client #%d: ", connid);
X	    perror ("select() error");
X	    close (server_sock);
X	    return;
X	  }
X	} while (value == -1 && errno == EINTR);
X	if (!FD_ISSET (msg_sock, &readfds))
X	  break;
X      }
X      if  (bytes_read <= 0) {
X	if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	    errno == ECONNREFUSED) {
X	  close (server_sock);
X	  return;
X	}
X	if (errno == EINTR)
X	  continue;
X	if (errno && errno != EAGAIN && errno != EWOULDBLOCK) {
X	  fprintf (stderr, "Client #%d: ", connid);
X	  perror ("Unexpected end-of-socket on client side");
X	  close (server_sock);
X	  return;
X	}
X      }
X      if (!strncmp (buf, "__abort__\n", 10)) {
X	close (server_sock);
X	return;
X      }
X    }
X    else if (FD_ISSET (server_sock, &readfds)) {
X      if (!total_bytes) {
X	for (; times; --times) {
X	  l = 0;
X	  do {
X	  again:
X	    if (read (sock_fd, &buf[l], 1) < 1) {
X	      if (errno == EBADF || errno == ECONNRESET ||
X		  errno == ECONNABORTED || errno == ECONNREFUSED)
X		return;
X	      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
X		goto again;
X	    }
X	    ++l;
X	  } while (buf[l - 1] != '\n');
X	  buf [l] = EOS;
X	  if (write_to_fd (msg_sock, buf, l) < 0) {
X	    fprintf (stderr, "Client #%d: write-to-client socket failed\n",
X		     connid);
X	    close (server_sock);
X	    return;
X	  }
X	  if (times == 2) { /* Handshake */
X	    sscanf (buf, "%d %d", &cookie, &value);
X	    if (cookie != CONNECT) {
X	      CANNOT_CONNECT ("Not a UNIX ListServer", CONN_ABORTED,
X			      "Handshake between gateway and server failed.\n");
X	      close (server_sock);
X	      return;
X	    }
X	    alarm (value + 30);		/* Just in case */
X	  }
X	}
X	times = 1;
X#ifdef DEBUG
X	printf ("Client #%d: reply code from server: %s", connid, buf);
X#endif
X	sscanf (buf, "%d %d\n", &value, &total_bytes);
X	die = FALSE;
X	if (value == PEER_UNAVAIL || value == CONN_CLOSED ||
X	    value == CONN_TIMEOUT || value == CONN_ABORTED ||
X	    value == SERVER_BUSY || value == SYS_ERROR)
X	  die = TRUE;
X      }
X      while (total_bytes > 0) {
X	if ((bytes_read = read (server_sock, buf, MIN (total_bytes, BUFFSIZ)))
X	    > 0) {
X	  if (write_to_fd (msg_sock, buf, bytes_read) < 0) {
X	    fprintf (stderr, "Client #%d: write-to-client socket error\n",
X		     connid);
X	    close (server_sock);
X	    return;
X	  }
X	  total_bytes -= bytes_read;
X	  if (total_bytes < 0)
X	    total_bytes = 0;
X	}
X	else if (bytes_read <= 0) {
X	  if (errno == EBADF || errno == ECONNRESET || errno == ECONNABORTED ||
X	      errno == ECONNREFUSED)
X	    return;
X	  if (errno == EINTR)
X	    continue;
X	  if (errno && errno != EAGAIN && errno != EWOULDBLOCK) {
X	    fprintf (stderr, "Client #%d: ", connid);
X	    perror ("Unexpected end-of-socket on server side");
X	    close (server_sock);
X	    return;
X	  }
X	}
X	FD_ZERO (&readfds); /* Work around a stupid AIX problem */
X#if defined (sequent) || defined (__NeXT__) || defined (__convex__) || \
X defined (apollo) || defined (unknown_port)
X	nfds = getdtablesize ();
X#else
X	nfds = ulimit (UL_GDESLIM);
X#endif
X	do {
X	  FD_SET (server_sock, &readfds);
X	  errno = 0;
X	  value = select ((nfds > 0 ? nfds : 20), &readfds, NULL,
X			  NULL, &timeout);
X	  if (value < 0) {
X	    fprintf (stderr, "Client #%d: ", connid);
X	    perror ("select() error");
X	    close (server_sock);
X	    return;
X	  }
X	} while (value == -1 && errno == EINTR);
X	if (!FD_ISSET (server_sock, &readfds))
X	  break;
X      }
X      if (die)
X	return;
X    }
X    else if (FD_ISSET (msg_sock, &exceptfds) ||
X	     FD_ISSET (server_sock, &exceptfds)) {
X      fprintf (stderr, "Client #%d: exception condition\n", connid);
X      close (server_sock);
X      return;
X    }
X  }
X}
X
X/*
X  Check whether the given host appears in a hosts file.
X*/
X
XBOOLEAN host_listed (char *hosts, char *host)
X{
X  char error [256];
X  char _host [256];
X  char line [256];
X  FILE* f;
X
X  OPEN_FILE (f, hosts, "r", "host_listed");
X  while (!feof (f)) {
X    RESET (_host);
X    fscanf (f, "%s", _host);
X    upcase (_host);
X    if (_host[0] != EOS && !strcmp (host, _host)) {
X      fclose (f);
X      return TRUE;
X    }
X  }
X  fclose (f);
X  return FALSE;
X}
X
X/*
X  Convert a string to upper case.
X*/
X
Xchar *upcase (char *s)
X{
X  char *r = s;
X
X  while (*s != EOS) {
X    if (islower (*s))
X      *s = toupper (*s);
X    ++s;
X  }
X  return r;
X}
X
X/*
X  Write to a non-blocking file descriptor.
X*/
X
Xint write_to_fd (int fd, char *buf, long int bytes_to_write)
X{
X  long int bytes_written, totalbytes = bytes_to_write;
X
X  errno = 0;
X  while ((bytes_written = write (fd, buf, bytes_to_write)) < bytes_to_write) {
X    if (errno && errno != EWOULDBLOCK && errno != EAGAIN) {
X      char error [256];
X      sprintf (error, "\nwrite_to_fd(): ");
X      switch (errno) {
X	case EBADF: strcat (error, "Bad file number"); break;
X	case EFAULT: strcat (error, "Bad address"); break;
X	case EFBIG: strcat (error, "File limit reached"); break;
X	case EINTR: continue;
X	case EINVAL: strcat (error, "Negative seek pointer"); break;
X	case EIO: strcat (error, "I/O error"); break;
X	case ENOSPC: strcat (error, "No space left on device"); break;
X	case ENXIO: strcat (error, "No such device or address"); break;
X	case ERANGE: sprintf (error + strlen (error), "Bytes to write (%d) \
Xout of range", bytes_to_write); break;
X	case EPIPE: sprintf (error, "Client disappeared"); break;
X	default: sprintf (error + strlen (error), "Error number %d", errno);
X      }
X      fprintf (stderr, "%s\n", error);
X      return -1;
X    }
X    if (bytes_written > 0)
X      bytes_to_write -= bytes_written,
X      buf += bytes_written;
X    errno = 0;
X  }
X  return totalbytes;
X}
*-*-END-of-gateway/gateway.c-*-*
echo x - gateway/gateway.h
sed 's/^X//' >gateway/gateway.h <<'*-*-END-of-gateway/gateway.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the files gateway.c
X  and gateway.h.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X  Use, duplication or disclosure by the Federal Government is subject to
X  the restrictions set forth in FAR 52.227-19(c), Commercial Computer
X  Software or, for Department of Defense Users, by DFAR 252.227-7013(c)(1)
X  (ii).
X
X  Below are the #define's pertinent to gateway.c
X*/
X
X#include "iulp.h"
X
X# ifndef MAX_CONNECTIONS
X#  define MAX_CONNECTIONS	5
X# endif
X#define BOOLEAN			unsigned int
X#define EOS			'\0'
X#ifndef TRUE
X# define TRUE			1
X#endif
X#ifndef FALSE
X# define FALSE			0
X#endif
X#define BUFFSIZ			8192
X#define RESET(var)		(var[0]) = EOS
X#define SERVICE			"ulistserv"
X#define PRIVILEGED_HOSTSF	"priv.hosts"
X#define UNWANTED_HOSTSF		"unwanted.hosts"
X
X#ifndef MIN
X# define MIN(a, b)		((a) < (b) ? (a) : (b))
X#endif
X
X#ifndef UL_GDESLIM
X# define UL_GDESLIM	4
X#endif
X#ifndef FD_SET          /* for 4.2BSD */
X# define FD_SETSIZE      (sizeof(fd_set) * 8)
X# define FD_SET(n, p)    (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
X# define FD_CLR(n, p)    (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
X# define FD_ISSET(n, p)  (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
X# define FD_ZERO(p)      memset ((char *)(p), '\0', sizeof(*(p)))
X#endif
X
X#ifndef WAIT_NEEDS_UNION
X# if defined (sequent) || defined (stardent) || defined (stellar) || \
X  defined (titan) || defined (unknown_port)
X#  ifdef WEXITSTATUS
X#   undef WEXITSTATUS
X#  endif
X#  ifdef WTERMSIG
X#   undef WTERMSIG
X#  endif
X#  ifdef WIFSIGNALED
X#   undef WIFSIGNALED
X#  endif
X#  ifdef WIFEXITED
X#   undef WIFEXITED
X#  endif
X# endif 
X#endif
X
X#ifndef WEXITSTATUS
X# define  WEXITSTATUS(stat)	((int)(((stat)>>8)&0377))
X#endif
X#ifndef WTERMSIG
X# define WTERMSIG(stat)		(((int)((stat)&0377))&0177)
X#endif
X#ifndef WIFSIGNALED
X# define WIFSIGNALED(stat)	(((int)((stat)&0377))>0&&((int)(((stat)>>8)&\
X				0377))==0)
X#endif
X#ifndef WIFEXITED
X# define WIFEXITED(stat)	(((int)((stat)&0377))==0)
X#endif
X
X#define SERVER_BUSY_ \
X"All interactive UNIX ListServer gateway ports are busy. Please try again \
Xlater.\n"
X
X#define CONN_TIMED_OUT \
X"Gateway connection timed out.\n"
X
X#define SERVER_SHUTS_DOWN \
X"Gateway shutting down.\n"
X
X#define REMOTE_SYS_ERROR \
X"Remote system error: UNIX ListServer gateway closing connection.\n"
X
X#define CLIENT_LOST(exitcode) \
Xclose (msg_sock),\
X_exit (exitcode)  
X
X#define REPLY(msg_sock, code, bytes)\
X{\
X  sprintf (reply, "%d %d \n", code, (bytes));\
X  if (write_to_fd (msg_sock, reply, strlen (reply)) < 0)\
X    CLIENT_LOST (13);\
X}
X 
X#define REPLY_FILE(msg_sock, code, bytes, file)\
X{\
X  sprintf (reply, "%d %d %s\n", code, (bytes), file);\
X  if (write_to_fd (msg_sock, reply, strlen (reply)) < 0)\
X    CLIENT_LOST (13);\
X}
X
X#define CLOSE_CLIENT(msg_sock) \
X  if (msg_sock >= 0) {\
X    REPLY (msg_sock, CONN_CLOSED, strlen (GOOD_BYE));\
X    write_to_fd (msg_sock, GOOD_BYE, strlen (GOOD_BYE));\
X    close (msg_sock);\
X  }
X
X#define CANNOT_CONNECT(__msg__, __code__, __reply__) \
X{\
X  char msg [256];\
X  sprintf (msg, "%d 10 6.0 1 1\n%d %d \n%s", CONNECT, __code__,\
X	   strlen (__reply__), __reply__);\
X  write_to_fd (msg_sock, msg, strlen (msg));\
X  close (msg_sock);\
X  printf (__msg__);\
X}
X
X#define OPEN_FILE(fp, file, mode, func) \
X  if ((fp = fopen (file, mode)) == NULL)\
X    printf ("\n%s(): Could not open %s: errno %d\n", func, file, errno),\
X    exit (1)
X
Xtypedef struct {
X  long int pid,			/* Client pid */
X  	   time,		/* Time connection established */
X  	   connid;		/* Client id */
X  char name [256];		/* Remote host's IP address or name */
X} CLIENT;
X
XCLIENT clients [MAX_CONNECTIONS];
X  
Xlong int nforks, sock_fd, msg_sock, server_sock;
Xint connid;
*-*-END-of-gateway/gateway.h-*-*
echo x - gateway/Makefile
sed 's/^X//' >gateway/Makefile <<'*-*-END-of-gateway/Makefile-*-*'
X# Makefile for the Interactive UNIX ListServer gateway.
X# Compile with -Dsvr4 if you are on a SYSVR4 system, -Dsvr3 if on an
X# SYSVR3 system, or -Dbsd. Also, compile with -DHAVE_SELECT_H if you have the
X# <sys/select.h> header file, and -DHAVE_POLL if your system has the poll()
X# system call.
X#
X# If your wait() needs a union as its first argument, compile with
X# -DWAIT_NEEDS_UNION
X#
X# If you do not have access to /etc/services, you may specify a port by
X# compiling with -DIUL_PORT=port. To override the default maximum number of
X# connections (5) compile with -DMAX_CONNECTIONS=number.
X#
X# Install the executable on a host that will play the role of the gateway.
X# The executable is setuid root to be able to obtain the privileged port
X# that the system uses; if an non-privileged port is used then remove the
X# 'su' below.
X#
X# See the ../src/Makefile if you have to use unproto and for other compile
X# options.
X
X# define your compiler:
XCC		= cc
X
X# define special symbols (system, compiler, etc):
XDEFINES		= -Dsvr4 -DHAVE_SELECT_H -DHAVE_ULIMIT_H -DHAVE_POLL
X
X# define the optimization level
XOPTIMIZATION	= -g
X
X# define where the preprocessor is:
XCPP		= /lib/cpp
X
X# unproto specific definitions -- use either COMP symbol (second is preferred):
X#SRC		= /tmp/$<
X#COMP		= cat $< | unproto > $(SRC); $(CC)
X#COMP		= $(CPP) $(DEFINES) $< | unproto > $(SRC); $(CC)
X
X# default definitions:
XSRC		= $<
XCOMP		= $(CC)
X
X# define linking options and libraries
XLDFLAGS		= $(OPTIMIZATION)
XLIBS		=
X
X# Nothing to change below
XCFLAGS		= -c $(OPTIMIZATION) $(DEFINES)
XLD		= $(CC)
X
Xall:	gateway
X	touch unwanted.hosts priv.hosts
X	su root -c 'chown root gateway; chmod 700 gateway; chmod u+s gateway'
X
Xclean:
X	rm -f gateway *.o
X
Xgateway:	gateway.o
X	rm -f gateway
X	$(LD) $(LDFLAGS) $@.o -o $@ $(LIBS) -lm
X	@rm -f /tmp/$@.c
X
Xgateway.o:	gateway.c gateway.h iulp.h
X	$(COMP) $(CFLAGS) $(SRC)
*-*-END-of-gateway/Makefile-*-*
echo x - gateway/README
sed 's/^X//' >gateway/README <<'*-*-END-of-gateway/README-*-*'
X
X
X
X
X			Interactive UNIX ListServer Gateway
X			-----------------------------------
X
X
X			     Anastasios C. Kotskikonas
X
X				    March 29 1993
X
XPURPOSE:
X--------
X
XIf you are going to use the interactive part of UNIX ListServer but the host
Xthat your system runs on is not on the Internet, you may install this
Xgateway on a host that is, and all connections coming to the gateway will be
Xforwarded to the host that the actual system runs on.
X
XINSTALLATION:
X-------------
X
XCopy this entire directory on the gateway host and type 'make'. The program
Xis written in ANSI C. The files 'unwanted.hosts' and 'priv.hosts' may be
Xused as explained in the documentation for the rest of the system.
X
XInteractive UNIX ListServer uses the privileged port number 372 for connections.
XOn the gateway host, put the following line in your /etc/services file:
X
Xulistserv	372/tcp
X
XNote that this step has to be done on the host that the actual interactive
Xsystem runs on as well. The Makefile will setuid the executable to root. If a
Xnon-privileged port is used then root access is not necessary.
X
XUSAGE:
X------
X
XOn the gateway host, run as follows:
X
X# gateway host port > some-log-file 2&>1 &
X
Xwhere 'host' is the host that runs this ListServer, and 'port' will most
Xlikely be 372.
X
XRESTRICTIONS:
X-------------
X
XA maximum of 5 connections will be accepted, so the system cannot be used
Xwith inetd.
X
XAlthough gateways may be chain-connected, no loop detection is performed. Be
Xcareful.
*-*-END-of-gateway/README-*-*
echo x - gateway/iulp.h
sed 's/^X//' >gateway/iulp.h <<'*-*-END-of-gateway/iulp.h-*-*'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the fileis gateway.h;
X  and gateway.c.
X  This software comes with no warranties and cannot be sold for profit.
X  The AGREEMENT and COPYRIGHT notices should be included in all source
X  files when distributing this software.
X  COPYRIGHT: Copyright (c) 1991-93, Anastasios C. Kotsikonas
X
X  Define the Interactive UNIX ListServer Protocol.
X*/
X
X#define OK                      100
X#define MORE_INPUT_REQUIRED	101
X#define CONTINUED		102
X#define PASSWORD_REQUIRED	103
X#define CONNECT			105
X#define MESSAGE			106
X#define WRITE_TO_FILE_ASC	110
X#define WRITE_TO_FILE_BIN	120
X#define APPEND_TO_FILE_ASC	130
X#define APPEND_TO_FILE_BIN	140
X#define TEST_FILE_PERMISSIONS	154
X#define SYNTAX_ERROR            200
X#define INVALID_REQ             300
X#define PEER_UNAVAIL            400
X#define BAD_ARCHIVE             500
X#define RESTRICTED_REQ          600
X#define NOT_OWNER               700
X#define SYS_ERROR               800
X#define PERMISSION_DENIED	860
X#define SERVER_BUSY             870
X#define CONN_TIMEOUT		880
X#define CONN_ABORTED		890
X#define CONN_CLOSED		900
*-*-END-of-gateway/iulp.h-*-*
echo x - IULP
sed 's/^X//' >IULP <<'*-*-END-of-IULP-*-*'
X
X
X
X		Interactive UNIX ListServer Protocol (IULP)
X		-------------------------------------------
X
XDate: April 4 1993
XAuthor: Anastasios Kotsikonas
X
XPURPOSE
X-------
X
XThis document attempts to explain the protocol used by an Interactive
XUNIX ListServer. Comments are most welcome. 
X
XOVERVIEW
X--------
X
XUNIX ListServer is an automated mailing list and archive system, supported
Xonly on UNIX platforms. By default, all communication is performed over
Xelectronic mail. Versions 6.0 and above are also capable of accepting
Xlive TCP/IP connections over the Internet, for processing of user requests.
XThe privileged port #372 has been assigned by the IANA for this purpose.
XThis document attempts to clarify the protocol between server and client,
Xand although a client is provided to users by the author, better ones
Xare possible.
X
XSERVER/CLIENT HANDSHAKE
X-----------------------
X
XUpon connection establishment a handshake operation takes place: the
Xserver reports to the client the following parameters, all numbers, in
Xa single line terminated by a newline character (\n):
X
Xmagic-cookie timeout version primary-prompt-length continuation-prompt-length
X
XWhere 'magic-cookie' is a 3-digit number common to both sides so that the
Xclient knows that it has connected to the right server. This number may
Xbe anything under 400 (currently set at 105, see below). If the 'magic-cookie'
Xis over 400 (inclusive) no connection could be established. 'Timeout' is
Xreported in seconds, and represents the time after which the server may
Xarbitrarily break the connection. The server's version is provided for future
Xuse. The system uses a primary prompt and a secondary one when a request is
Xeither to be continued on the next line, or its arguments are not known.
X
XSERVER/CLIENT COMMUNICATION
X---------------------------
X
XEach server/client transaction is based on server reply codes. For each
Xclient request (i.e. each string terminated by a newline character) a
Xreply is generated by the server (except during login where more replies are
Xgenerated -- see below, and in the case ehere the server sends status reports
Xusually on delays); this reply may be followed by any number of lines of text.
X
XServer replies have of the following format:
X
X<Control string>\n
X[Optional ListServer text ...
X.
X.
X.
Xor file\n]
X
XA control string always precedes the actual message/file sent by ListServer.
XThis string spans one line and is terminated by a newline character (\n).
XThe control string consists of a 3-digit reply code, followed by a blank,
Xfollowed by the number of bytes of the actual message/file (>=0), followed
Xby a blank, optionally followed by a file name (if ListServer is to send a
Xfile), and terminated by a newline character:
X
Xxyz #of-bytes [filename]\n
X
XEach reply code digit further refines the meaning of the previous one,
Xas shown below:
X
X
X	x	|	y	|	z
X------------------------------------------------	
X1: OK		| 0: echo to	| 0: no more
X		|    tty	|    input req
X		|		|    (prompt)
X2: syntax error	| 1: write to	| 1: more input
X		|    file (asc)	|    required
X3: invld reqst	| 2: write to	|    (no prompt)
X4: peer unavail	|    file (bin)	| 2: cmd cont on
X5: bad archive	| 3: append to	|    next line
X6: restr reqst	|    file (asc)	| 3: password req
X7: not owner	| 4: append to	| 4: reply req
X8: sys error	|    file (bin)	| 5: connect
X9: conn closed	| 5: test perm	| 6: message
X		| 6: permission	|
X		|    denied	|
X		| 7: sys busy	|
X		| 8: timeout	|
X		| 9: conn	|
X		|    aborted	|
X
X
XNot all combinations make sense, and the ones that currently have a meaning
Xare listed below:
X
X
X	x	| 	y	| 	z
X------------------------------------------------
X	1	|	0	|	0
X		|		|	1
X		|		|	2
X		|		|	3
X		|		|	5
X		|		|	6
X		|		|
X		|	1	|	0
X		|		|
X		|	2	|	0
X		|		|
X		|	3	|	0
X		|		|
X		|	4	|	0
X		|		|
X		|	5	|	4
X		|		|
X	2	|	0	|	0
X		|		|
X	3	|	0	|	0
X		|		|
X	4	|	0	|	0
X		|		|
X	5	|	0	|	0
X		|		|
X	6	|	0	|	0
X		|		|
X	7	|	0	|	0
X		|		|
X	8	|	6	|	0
X		|		|
X		|	7	|	0
X		|		|
X		|	8	|	0
X		|		|
X		|	9	|	0
X		|		|
X	9	|	0	|	0
X
XThe reply codes currently used have the following meanings:
X
X100 OK
X101 More input required					      (for put request)
X102 Request continued on next line		     (line terminated with &\n)
X103 Password required						  (after login)
X105 Connect							 (magic cookie)
X106 Message		  (server intermittent message before ListServer reply)
X110 Write to file							(ascii)
X120 Write to file						       (binary)
X130 Append to file							(ascii)
X140 Append to file						       (binary)
X154 Test file permissions			(server asks client to confirm)
X200 Syntax error					   (ill-formed request)
X300 Invalid request					 (unrecognized request)
X400 Peer unavailable		       (network loop, peer server/gateway down)
X500 Bad archive					  (covers all archive problems)
X600 Restricted request		(request has to be resubmitted at a later time)
X700 Not owner				     (owner request not from the owner)
X800 System error		  (server/gateway aborted/various other errors)
X860 Permission denied		   (cannot write/append to file on client side)
X870 Server busy						 (too many connections)
X880 Connection timed out				      (timeout expired)
X890 Connection aborted					  (for various reasons)
X900 Connection closed					   (normal termination)
X
XSERVER/SERVER COMMUNICATION
X---------------------------
X
XA user request may refer to a list that is served by a remote server known
Xto this server; since list names may not be unique across all servers, the user
Xmay specify a full electronic mail address for that list to distinguish it
Xfrom the others. It is the local server's responsibility to chain-connect to
Xall remote server(s), get the reply/replies and forward it/them back to the
Xuser. This chain-connection is currently limited to 5 hops, but is system-
Xconfigurable. Each participating UNIX ListServer installation keeps a database
Xof all known servers, their electronic mail addresses, their DNS or IP
Xaddresses, and the ports they are listening to. If chain-connection cannot be
Xestablished, the user will have to resort to electronic mail; he may send
Xhis request to this server which will in turn forward it to the proper one(s).
X
XThe protocol used during a server-to-server connection is identical to the
Xone described above.
X
XCONNECTION ESTABLISHMENT
X------------------------
X
XUpon a successful handshake, the server initiates the login process,
Xwhere the user may identify himself by providing an email address as
Xsubscribed to one of the lists (if any). The control string in this case is:
X
X100 #of-bytes-of-greeting+email-prompt
X
XDepending on the user reply, the server either asks for a password (the
Xuser provides the password used in the list for which the email address
Xrefers to), if the user did indeed specify an email address and then
Xproceeds to the main prompt, or reports OK and proceeds to the main prompt, if
Xno email address was specified.
X
XIn the former case, the first control string is:
X
X103 #of-bytes-of-password-prompt
X
Xand the latter:
X
X100 #of-bytes-of-help+prompt
X
XIn the latter case, the first control string reads:
X
X100 0
X
Xand the second:
X
X100 #of-bytes-of-help+prompt
X
XAfter the handshake, the server may deny access by providing a "Server Busy",
X"Connection Aborted", "Peer Unavailable", "Connection Timed Out" or "System
XError" reply:
X
X400/800/870/880/890 #of-bytes-of-text
X
Xand close the connection. It reports "Peer Unavailable" (400) if it detects a
Xloop in the chain of live UNIX ListServer connections, or if a gateway could
Xnot connect to the actual server.
X
XREQUEST PROCESSING
X------------------
X
XMost requests/replies require a single server reply (whether or not the
Xrequest is valid). However, the server may send status reports on anticipated
Xdelays when it is busy processing other requests; in this case, the client
Xis required to echo the message and keep polling for the actual reply to the
Xuser's request. Since requests may be continued on multiple lines, the server
Xmay issue continuation commands in the control string (102) and use the
Xsecondary prompt. For example:
X
Xrequest> HELP &
X102 2
X> SET
X100 1042
X...
Xrequest>
X
XThe number of bytes following the reply code includes the primary prompt's
Xlength, and the actual reply contains the prompt string at the end in all cases.
X
XWhen the reply is to be redirected to a file however (as is the case during
Xa "get" request, or when explicitly redirecting the output), the server
Xrequests from the client to verify that the output file has write permissions
X(by providing the file name) and expects from the client "Permission Denied"
X(860) or a go ahead:
X
X154 0 filename
X
XFor example:
X
Xrequest> HELP SET > OUT
X154 0 out
X
XAt this point, the client may send a 100 reply code and the server counter
Xreplies with:
X
Xreply-code #of-bytes-of-file/text+prompt filename
X
XThis reply code must tell the client that the transfer is destined for
Xa file and must identify the type of transfer (ascii or binary).
XSince the prompt is part of the reply, the client must isolate it from the
Xstream. If the client reports "Permission Denied" the server simply counter
Xreplies with:
X
X100 #length-of-prompt
X
XCertain requests (like put) take an indefinite number of lines of text
Xas argument. For these requests, the server first replies with:
X
X101 #of-bytes-of-explanatory-text
X
Xand expects input to finish with a single period ('.') in a line by itself.
XFor every such input line except the-end-of-input ('.') the server either
Xreports:
X
X100 0
X
Xor in the case thit line is continued on another line:
X
X102 #length-of-secondary-prompt
X
XAt any point throughout the session, the server may abort, close or time
Xout the connection.
X
XEXAMPLES
X--------
X
XIn the example session below, user input is capitalized:
X
X% telnet cs.bu.edu 7000
XTrying...
XConnected to cs.bu.edu.
XEscape character is '^]'.
X
X105 600 6.0 9 2
X100 684
X
XBoston University experimental interactive UNIX ListServer.
XVersion 6.0 Mon Mar 15 11:20:28 1993
XMaximum connection duration is 600 seconds.
X
XWelcome to Boston University's experimental UNIX ListServer; this service is
Xprovided as training ground and may be used freely.
X
XTo log in as a list's owner provide:
XFor list venus: email address 'tasos', password 'venus1'
XFor list herc: email address 'tasos', password 'herc1'
X
XTo log in as a subscriber:
XFor any list: email 'tasos', password '007'
X
XREMINDER:
Xquit: end the session at any time.
X? or privileges: get a listing of all valid requests you may issue.
Xtimeleft: get the remaining time of the connection.
X
Xemail address [none]: TASOS
X103 10
XPassword: VENUS1
X100 1342
XYou have logged in with owner privileges; valid requests are:
X
Xhelp [topic]
Xset <list> [<option> <arg[s]>] -
X        option:mail, password, address, conceal
X.
X.
X.
Xrequest> HELO
X106 Server busy processing another request; please wait...
X300 43
X>helo
X
XUnrecognized request HELO
X
Xrequest> PUT VENUS VENUS1 &
X102 2
X> SUBSCR
X101 55
X[Enter text below, end with a '.' in a line by itself]
XTASOS ACK PASSWORD NO TASOS
X100 0
XJOHN ACK PASSWORD NO &
X102 2
X> JOHN
X100 0
X.
X300 80
X>PUT VENUS XXXXXX
X
XInvalid or missing keyword for PUT request
X
Xrequest> GET LISTSERVER INFO
X154 0 info
X100
X110 59 info
XPlace to put information for the LISTSERVER archive
Xrequest> REPORTS HERC HERC1
X700 67
X>REPORTS HERC XXXXX
X
XYou are not the owner of this list
X
Xrequest> QUIT
X900 36
XUNIX ListServer closing connection.
*-*-END-of-IULP-*-*
echo x - upgrade_to_6.0
sed 's/^X//' >upgrade_to_6.0 <<'*-*-END-of-upgrade_to_6.0-*-*'
X#!/bin/sh
X
X# Determine how echo suppresses new-line
Xecho "a\c" > /tmp/echo
Xif [ "`grep c /tmp/echo`" = "a\c" ]; then
X  n='-n'
X  c=''
Xelse
X  n=''
X  c='\c'
Xfi
Xrm /tmp/echo
X
Xecho
Xecho \	\	\	UNIX ListServer upgrade script
Xecho
Xecho This script is to be used ONLY to upgrade earlier versions to 6.0,
Xecho and is to be executed only once!!! If you have done this before, or have
Xecho $n "NOT run 'setup' yet hit ^C now. Otherwise hit return...$c"
Xread x
Xecho
Xecho Versions prior to 6.0 named their default archive \'listserv\'\; under 6.0 \
Xthe
Xecho default archive name is \'listserver\'. If your archives/ directory has been
Xecho moved to oarchives/, please restore your archives first before proceeding.
Xecho $n "Proceed [y]? $c"
Xread x
Xif [ "$x" = "y" -o "$x" = "Y" -o "$x" = "" ]; then
X  cd archives
X  if [ $? -ne 0 ]; then
X    echo Cannot cd to archives\; cannot procced.
X    exit 1
X  fi
X  if [ ! -d listserv ]; then
X    echo
X    echo Directory archives/listserv not found\; it looks like you either \
Xforgot to
X    echo restore your own archives, or you wish to use the canned ones. 
X    echo $n "Do you wish to use the canned ones [y]? $c"
X    read x
X    if [ "$x" = "n" -o "$x" = "N" ]; then
X      echo
X      echo OK. Aborting so you can restore your own archives.
X      exit 1
X    fi
X  else
X    echo
X    mv listserv listserver
X    ln -s listserver listserv
X    for i in `find . -name DIR -print` `find . -name INDEX -print`; do
X      egrep -i '\/listserv|^listserv' $i > /dev/null 2>&1
X      if [ $? -eq 0 ]; then
X        echo Updating $i
X        sed s@\/listserv@\/listserver@g $i | sed s@LISTSERV@LISTSERVER@g > ${i}~
X        test -s ${i}~ && mv ${i}~ $i
X      fi
X    done
X  fi
X  cd ..
Xfi
Xecho
Xecho Versions prior to 6.0 have only one attribute, the MAIL mode for each
Xecho subscriber in the .subscribers files. In this version, there are 2 new
Xecho attributes: PASSWORD and CONCEAL. The PASSWORD attribute is used when \
Xusers
Xecho connect to an interactive UNIX ListServer, and to allow users to reset the
Xecho address they are subscribed with. The CONCEAL attribute is used to protect
Xecho the users\' identity from \'recipients\' and \'statistics\' requests.
Xecho
Xecho Each subscriber will be assigned a random password and he may find out \
Xwhat
Xecho it is via a \'SET \<list\>\' request. This password may be reset via \
Xanother
Xecho SET request. Each subscriber will also have its CONCEAL attribute set to \
XNO.
Xecho
Xecho The script will now scan the lists/ directory and add passwords and conceal
Xecho attributes to all .subscribers files.
Xecho $n "Proceed [y]? $c"
Xread x
Xif [ "$x" = "n" -o "$x" = "N" ]; then
X  exit 0
Xfi
X
X# Extract the HOMEDIR definition from the Makefile
Xif [ ! -f src/Makefile ]; then
X  echo No src/Makefile found\; cannot proceed.
X  exit 2
Xfi
XHOMEDIR=`sed -n 's/^[	 ]*HOMEDIR[	 ]*=[	 ]*//p' src/Makefile`
X
X# Extract the current HOMEDIR
Xif [ ! -f src/ansi/defs.h ]; then
X  echo File src/ansi/defs.h not found\; cannot proceed.
X  exit 2
Xfi
XCURRDIR=`sed -n 's/^#[ 	]*define[	 ]*PATH[	 ]*\"//p' \
Xsrc/ansi/defs.h | tr -d '"'`
X
Xif [ "$HOMEDIR" != "$CURRDIR" ]; then
X  echo You have to run \'setup\' first.
X  exit 2
Xfi
X
Xif [ ! -d $HOMEDIR ]; then
X  echo $HOMEDIR: not a valid path\; cannot proceed.
X  exit 2
Xfi
Xcd $HOMEDIR/lists
Xif [ $? -ne 0 ]; then
X  echo Cannot cd to $HOMEDIR/lists\; cannot procced.
X  exit 1
Xfi
Xecho
Xpass=701230365
Xfor i in `\ls`
Xdo
X  echo \*\*\* Upgrading list $i \*\*\*
X  if [ ! -f $i/.subscribers ]; then
X    echo No .subscribers file found for list $i
X  else
X    mv $i/.subscribers $i/.subscribers.old
X    awk '{ print $1 " " $2 " " pass " NO " $3 " " $4 " " $5 " " $6 " " $7 " " \
X$8; pass=pass+2783 }' pass=$pass $i/.subscribers.old > $i/.subscribers
X    pass=`expr $pass + 23984`
X  fi
Xdone
Xecho
Xecho Now that the upgrade is done, may I remove myself so you won\'t be tempted
Xecho $n "to do it again [n]? $c"
Xread x
Xif [ "$x" = "y" -o "$x" = "Y" ]; then
X  rm -i $HOMEDIR/$0
Xfi
Xexit 0
*-*-END-of-upgrade_to_6.0-*-*
echo x - README
sed 's/^X//' >README <<'*-*-END-of-README-*-*'
X
X
X                       UNIX LISTSERVER SYSTEM
X                       ----------------------
X
X           Copyright (c) 1991-93, Anastasios C. Kotsikonas
X
X                           tasos@cs.bu.edu
X			    April 4 1993
X
XDESCRIPTION:
X     This is a system that implements various mailing lists  with
X     one  list  server. It is automated, and obliterates the need
X     for user intervention and maintenance of multiple aliases of
X     the  form  "list,  list-owner,  list-request", etc. There is
X     support  provided  for  public  and   private   hierarchical
X     archives,  moderated  and  non-moderated  lists, peer lists,
X     peer servers, private lists, address aliasing, news  connec-
X     tions  and gateways, mail queueing, digests, list ownership,
X     owner preferences, crash recovery, batch processing,  confi-
X     gurable headers, regular expressions, archive searching, and
X     live user connections via TCP/IP.
X
XINSTALLATION DIRECTORY:
XBe aware that the system relies heavily on locks. Certain hosts do not support
Xfile locking over NFS, so it is suggested that you do not install on an NFS
Xmounted file system unless file locking is indeed supported over NFS. To cover
Xthe generic case, certain ports have file locking turned off if it is not
Xsupported over NFS; see src/defs.h. If installing on a local file system then
Xyou should remove the definition of the NO_LOCKS symbol from src/defs.h. See
Xalso src/Makefile and src/README.
X
XSYSTEM SETUP:
XIf this is your first installation then here are a few things you need to know:
X
X0) Run the 'systest' script which will analyze your system and make suggestions
X   about the various system parameters.
X
X1) Do not compile by going to the src directory and doing a make. Instead,
X   edit the src/Makefile and see if there are any changes to be made; in
X   particular do not forget to define the HOMEDIR, the directory where the
X   system is installed. Do not yet compile; you have a long way to go.
X
X2) Read the src/README file.
X
X3) If you installed the system just anywhere, keep in mind that a separate
X   account with user name "server" is required. The system should be installed
X   under that account.
X
X4) Root access is required in a few places, like setting up mail aliases, for
X   a complete system, and defining the new user account.
X
X5) The documentation is huge and you can't wait to compile. First read the
X   main man page in the doc directory: server.nr. Look for the PORT SPECIFIC
X   section near the end to see if there are special guidelines for your host.
X
X6) Now you may be in a position to build the system: run the script 'setup'
X   or simply type 'make'. Do NOT go into src/ and type 'make'. The 'setup'
X   script is ALL you need to have a working system.
X
X7) If the build finished without any problems you are ready to finish the rest
X   of the doc; otherwise further tweaking is required. Contact me for help.
X
X8) The SYSTEM SETUP section in doc/server.nr should be read carefully now.
X
X9) Go make some coffee; it'll take you a long time to figure everything out.
X   Like one of the users said, "You do not know anything until you know
X   everything."
X
X10) When done, please send me the src/REGISTRATION.
X
XFILES:
XThe top level directory contains some files that you need to tweak for a fully
Xworking system, after the build has completed. These are:
X
X- config: system configuration (see doc/server.nr).
X- owners: definition of mailing lists' owners (see doc/listserv.nr).
X- unwanted.hosts: host names or IP addresses not allowed to connect to your
X    live system.
X- priv.hosts: privileged host names or IP addresses that can connect to your
X    live system; all other hosts are rejected.
X- welcome.live: welcoming message upon live connection establishment.
X
XOther files you may need to tweak:
X- help/live: it's a shell script and some of the commands in it may not
X    be present on your system; issue a "help live" request or execute
X    'sh help/live' to see if the script works OK.
X- src/Makefile: host specific definitions needed.
X
XFor a brief description of all files included in this package see src/README.
X
XEnjoy!
*-*-END-of-README-*-*
echo x - FAQ
sed 's/^X//' >FAQ <<'*-*-END-of-FAQ-*-*'
X
X			Frequently Asked Questions
X			--------------------------
X
XContents:
X---------
X1) Why does ListServer strip names given to 'subscribe'?
X2) Why does ListServer put 'X-Comment:' header lines and not the standard
X   'Comments:'?
X3) Why does ListServer use the 'From ' address instead of 'From:'?
X4) Why does ListServer put the list's address in the 'Sender:' header line?
X5) Is ListServer fully RFC-822 compliant?
X6) When receiving a news feed, the body of the message contains all the header
X   lines, and the distributed message has an empty 'Subject:' field.
X7) The return mails generated by listserv.c and list.c contain two or
X   more Errors-To: fields.
X8) What are the relevant files the manager and owners should be periodically
X   checking?
X9) OK, now I installed the software, what do I do next?
X10) What does this software do?
X11) I am the owner of the list and my approvals get rejected saying that I am
X    not.
X12) How do I force replies to go to the original author and not the list?
X13) Although my list has multiple owners, only the primary owner receives
X    subscription approval requests, is forwarded messages for approval, and is
X    copied according to his preferences.
X14) Some users don't get postings, but I can't say how often.
X****
X
X1) Why does ListServer strip names given to 'subscribe'?
X
XThe listings for 'recipients' and 'statistics' requests are compiled by running
Xshell scripts. The system strips names for security reasons (we do not want
Xbackquote characters to be passed to the scripts), and to avoid syntax errors
X(characters like <, > and | may cause problems); the system preserves '-',
X'@' and '.'. The system also converts the wild characters * and ? to #, and /
Xto % for security reasons again.
X
X2) Why does ListServer put 'X-Comment:' header lines and not the standard
X   'Comments:'?
X
XListServer prefers to use proprietary header lines because in version 6.0
Xconfigurable headers will be supported. This means that the system will be
Xable to carry over header lines from the original sender's message, and these
Xmay include 'Comments:'
X
X3) Why does ListServer use the 'From ' address instead of 'From:'?
X
XPrimarily for security reasons, i.e. to reduce the likelyhood of forged email.
XIt also makes the algorithm easier, it enables peer lists to work, and does
Xnot have to parse multi-line 'From:' lines to look for the address. The system
Xprovides the aliases files for dealing with 'From ' addresses that cause
Xproblems.
X
X4) Why does ListServer put the list's address in the 'Sender:' header line?
X
XSo that error messages sent to this address are properly archived; these
Xmessages are then sent to the owner and are internally scanned for recognizable
Xerror conditions; automatic is then taken (subscribers are suspended or removed
Xfrom the system files). RFC-822 requires that this address is the actual
Xaccount where the mail originated from; the author does not agree with this,
Xsince this would imply the 'server' account (i.e. the manager of the system),
Xand the owner would not see these error messages in this case.
X
X5) Is ListServer fully RFC-822 compliant?
X
XNo. It does not accept addresses in the 'From ' line that contain backquotes
Xand wild characters, for security reasons. It also does not support multi-line
Xheader lines, and uses non-standard header lines.
X
X6) When receiving a news feed, the body of the message contains all the header
X   lines, and the distributed message has an empty 'Subject:' field.
X
XYou have not set up things properly and the list does not receive the article
Xin email format; instead the gateway or news-feed program sends the article in
Xa mail message, which is a different case.
X
X7) The return mails generated by listserv.c and list.c contain two or
X   more Errors-To: fields.
X
XComment out the following line from your sendmail.cf (or whatever have you):
X
XHErrors-To:
X
XOr turn them off completely by compiling with -DNO_ERRORS_TO.
X
X8) What are the relevant files the manager and owners should be periodically
X   checking?
X
Xa) I check /usr/server/.report.* and /usr/server/lists/*/.report.list on a
X   daily basis.
Xb) The various mbox files (/usr/server/mbox, /usr/server/lists/*/mbox) should
X   be cleaned periodically.
Xc) The /usr/server/mqueue directory should be periodically checked to see
X   if there are any files sitting in there waiting to be delivered.
X
X9) OK, now I installed the software, what do I do next?
X
XRead the README file in this directory.
X
X10) What does this software do?
X
XOVERVIEW
X     This is a system that implements various mailing lists  with
X     one  list  server. It is automated, and obliterates the need
X     for user intervention and maintenance of multiple aliases of
X     the  form  "list,  list-owner,  list-request", etc. There is
X     support  provided  for  public  and   private   hierarchical
X     archives,  moderated  and  non-moderated  lists, peer lists,
X     peer servers, private lists, address aliasing, news  connec-
X     tions  and gateways, mail queueing, digests, list ownership,
X     owner preferences, crash recovery, batch processing,  confi-
X     gurable headers, regular expressions, archive searching, and
X     live user connections via TCP/IP.
X
X11) I am the owner of the list and my approvals get rejected saying that I am
X    not.
X
XThe system's manager has not updated the owners file; he needs to put your
Xaddress (from the "From " line) in that file.
X
X12) How do I force replies to go to the original author and not the list?
X
XUse the -P flag to your list's definition in the config file. Then depending
Xon the user's mailer, he will be able to "re"ply to the author, or "Re"ply
Xto the list.
X
X13) Although my list has multiple owners, only the primary owner receives
X    subscription approval requests, is forwarded messages for approval, and is
X    copied according to his preferences.
X
XThat's the intended behavior to avoid miscommunication between the owners
X(multiple subscription approvals, simultaneous rejection and approval of
Xmessages in moderated lists, etc). You may define a mail alias containing
Xall of the owners' addresses and make this alias the primary owner to have
Xall owners copied.
X
X14) Some users don't get postings, but I can't say how often.
X
XMost likely there are problems with your sendmail; check the mqueue/ directory
Xfor files; if there are any, start queued; files are placed in that directory
Xwhen the system cannot connect to your sendmail or sendmail aborts. The report
Xfiles should be your guide.
*-*-END-of-FAQ-*-*
echo x - WHATS_NEW
sed 's/^X//' >WHATS_NEW <<'*-*-END-of-WHATS_NEW-*-*'
X
X
X			UNIX ListServer version 6.0
X			---------------------------
X
XHere's what's new with 6.0; it is 100% backward compatible but one place (see
X#10):
X
X1) Interactive system: serverd may be put to TCP/IP listening mode for live
X   connections, at port 372, via the -i command line option:
X
X   a) A special client has been written and can be found in archives/iul/
X      That directory contains the source code and the man page. Source and doc
X      can also be found under the src/ and doc/ dirs.
X   b) 'help live' explains a little more about this feature.
X   c) 'remote' lists may now specify a host and a port, so that your
X      interactive ListServer can chain-connect to others.
X   d) serverd has to be setuid to root to be able to get the privileged port;
X      once obtained, it sets uid back to server.
X   e) See the src/README file for system qualifications to use this feature,
X      doc/serverd.1 (of course), and run systest.
X
X2) The ULISTSERVER_UMASK environment variable is now supported to set file
X   access modes; define it in both .[t]cshrc AND .profile:
X   .[t]cshrc: setenv ULISTSERVER_UMASK 077
X   .profile:  ULISTSERVER_UMASK=077; export ULISTSERVER_UMASK
X
X3) The 'precedence' config directive is used to put a Precedence: header line.
X
X4) Configurable headers are supported via the 'header' config directive; this
X   lets you define which header lines from subscriber messages to save and
X   copy to the distributed message.
X
X5) Regular expressions (extended egrep(1)) are supported for the .ignored
X   and .aliases files; they can also be used in the MAILER_DAEMON and 
X   SUSP_SUBJECT symbols in src/defs.h:
X
X   a) UUCP has been reintroduced in src/defs.h as "UUCP"; you may alter
X      this to "^UUCP$" so that only mail 'From uucp Date Time' will be treated
X      in a special way.
X   b) In your .ignored files you should replace entries like "root" with
X      "^root", "sys" with "^sys", etc.
X
X6) Lists may have their messages automatically archived in a variety of forms;
X   see doc/server.1 for more information; the 'archive' config directive has
X   been introduced to specify the archive, directory to put files, optional
X   password, and the format of the file names. New directories and archives
X   are automatically created.
X
X7) New farch: 
X   a) create new directories and archives recursively; create all necessary
X      DIR and INDEX files; update all necessary ancestor INDEX files.
X   b) New option -B archives pure binaries without splitting and uuencoding;
X      a 'GET' request on such files forces a uuencode on the way out.
X   c) New option -D puts a descriptive message about the archived file in the
X      DIR file.
X   d) New option -r removes the specified files from the specified archive.
X   e) New option -p specifies a password for a private archive.
X
X8) Ability for users to execute UNIX commands on a per list basis via the
X   'unix_cmd' config directive and the 'run' request; see doc/server.1 and
X   doc/listserv.1 for more information.
X
X9) The config file now supports the '&\n' line continuation sequence used
X   for requests.
X
X10) The .subscribers file now requires two more attributes: a password used
X    during live connections, and the CONCEAL mode (YES or NO)
X    a) the upgrade_to_6.0 script should be executed to assign passwords and
X       default no-conceal modes to all subscribers of all lists.
X    b) 'set <list>' now reports the password and the conceal mode too, as well
X	as the address used for subscription.
X    c) 'set <list> password <old-password> <new-password>' is the request to
X       reset the password. New subscribers are assigned initial random
X       passwords.
X    d) 'set <list> address <personal-password> <new-address>' is the request
X       that lets a user reset the address he is subscribed with.
X    e) 'set <list> coneal yes|no' changes the conceal mode.
X
X11) The 'system' mailmethod now sends 'HELO hostname' to sendmail if the
X    system is compiled with -DZMAILER; tweaking of the code is required to
X    send 'HELO IP-address'. This is to be used only with the Zmailer only.
X
X12) Grouping of addresses by domains in the .subscribers files for faster
X    distribution.
X
X13) THANK and its foreign equivalents mark the end of requests.
X
X14) 'list' senses and rejects ListServer requests.
X
X15) The 'default' directive is introduced in the config file to select
X    default values for mailmode, address setting, password assignment,
X    conceal mode for each list.
X
X16) "Error during delivery: check the report files" messages now are more
X    verbose and include the error message in the body of the mail message;
X    no need to check the report files!
X
X17) Similar addresses are listed when a request or posting is rejected
X    due to subscription problems.
X
X18) Support for syslog(3) by compiling with -DSYSLOG=facility.
X
X19) Screening of mailer daemon error messages if compiling with
X    -DERROR_MAIL_ANALYSIS=level.
X
X20) Check for fake mail by comparing the domain in the user address against
X    the Received: lines.
X
X21) No Errors-To: lines if you compile with -DNO_ERRORS_TO.
X
X22) Man pages have been split.
X
X23) New "limit" in the config file: files -- limits size of files sent out
X    and automatically splits them to smaller subparts.
X
X24) Limit on the number of messages processed daily on a per list basis via the
X    "ceiling" config directive.
X
X25) System files have been restricted to 14 characters maximum.
X
X26) New "fax" request and config directive (faxes files instead of emailing
X    them.
X
X27) Concealed lists.
X
X28) Help/.welcome/.info files may now be shell scripts.
X
X29) Ability to search the archives via the "search" request.
*-*-END-of-WHATS_NEW-*-*
echo x - util/check_addr
sed 's/^X//' >util/check_addr <<'*-*-END-of-util/check_addr-*-*'
X#!/usr/local/bin/perl
X# This is a perl script that Mike Morse (mmorse@z.nsf.gov) wrote and is to
X# be used before catmail to check wether an incoming address can be handled
X# by your mailer.
X#
X# Tasos
X#
X# a script to check a mail address
X# returns 0 if OK (far as we know)
X# returns 1 if not.
X
X$debug = 0;
X$tmp = "/tmp/chkaddr$$";
Xif ($#ARGV != 0){
X   print("usage: check_addr address\n");
X   &leave(1);
X}
X$test = $ARGV[0];
X
X# actually, the first two tests, for 1) embedded spaces and
X# 2) no @ sign will never fail, because download prevents
X# spaces, and adds "@nsf.gov" to addresses without an @.
X
X# does it have spaces?
X$num_spaces = $test =~ tr/ / /;
Xif ($num_{
X   &leave(1);
X}
X
X# does it have an @?
X$num_ats = $test =~ tr/\@/\@/;
Xif ($num_ats != 1){
X   &leave(1);
X}
X
X# does it have a rhs and lhs?
X($lhs,$rhs) = split(/@/,$test);
Xif (! $lhs || ! $rhs) {     # must have both a rhs and lhs
X   &leave(1);
X}
X
X# does the rhs have dots?
X$num_dots = $rhs =~ tr/././;
Xif (! $num_dots) {
X   &leave(1);
X}
X
X# if it has just one dot, does it end with "BITNET"?
Xif ($num_dots == 1) {
X   if ($rhs =~ /\.bitnet$/i) {
X      &leave(0);
X   }
X}
X
X# OK, everything looks pretty good, let's try nslookup
Xopen(LOOK,"|/usr/etc/nslookup > $tmp 2>$tmp") || &leave(1);
Xprint LOOK<<EOM;
X$rhs
Xset querytype=MX
X$rhs
XEOM
Xclose(LOOK);
Xopen(IN,$tmp) || &leave(1);
Xwhile(<IN>){    # look at non-MX query
X   if (/^> >/){
X      last;
X   }
X   if (/^Name/) {
X      $try = <IN>;
X      if ($try =~ /^> >/){
X         last;               # shouldn't happen
X      }
X      if ($try =~ /^Address:\s+\d+\./) { # looks good
X         &leave(0);
X      }
X   }
X}
Xwhile(<IN>){    # look at MX query
X   chop;
X   if (/mail exchanger =/){
X      &leave(0);
X   }
X}
X&leave(1);
X
X
Xsub leave {
X   if($debug){
X      print("Exit $_[0].\n");
X   }
X   if( -e $tmp) {
X      unlink($tmp);
X   }
X   exit ($_[0]);
X}
*-*-END-of-util/check_addr-*-*
echo x - util/combine.c
sed 's/^X//' >util/combine.c <<'*-*-END-of-util/combine.c-*-*'
X/*
X  Purpose: extract files that have been received in multiple emails via a 'get'
X    request from a UNIX ListServer 6.0 and up.
X
X  Usage: combine <mail-box file>
X
X  How to use: Save all the parts received in a file, in any order; then run
X    combine on this file; it will extract the target file (by extracting all
X    parts, one after the other in the proper sequence). Uudecode will be
X    executed automatically if necessary. You have to have all parts.
X
X  Author: Tasos Kotsikonas (tasos@cs.bu.edu)
X  Date: February 16 1993.
X*/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#define EOS		'\0'
X#define LISTSERV	"X-Listserver-Version: 6."
X#define SUBJECT		"Subject:"
X#define CUT_HERE \
X"------------------------------ Cut here ------------------------------"
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X
X  FILE *fin, *fout = NULL;
X  char junk [80], line [1024], linecopy [1024], archive [1024], file [80],
X       nfile [80], req [80];
X  int nparts, part, prev_part, parts_done, nloops, subpart, prev_subpart,
X    nsubparts, subparts_done, uuencoded = 0, first_line = 1;
X  extern char *strchr();
X
X  if (argc < 2)
X    fprintf (stderr, "Usage: %s <mail-box file>\n", argv[0]),
X    exit (2);
X  if ((fin = fopen (argv[1], "r")) == NULL)
X    sprintf (junk, "Cannot open %s", argv[1]),
X    perror (junk),
X    exit (1);
X
X  nparts = part = prev_part = prev_subpart = nloops = subpart = prev_subpart =
X    nsubparts = subparts_done = 0;
X  parts_done = -1;
X  file [0] = EOS;
X  while (parts_done) {
X    while (!feof (fin)) {
X      line[0] = EOS;
X      fgets (line, 1023, fin);
X      if (!strncmp (line, SUBJECT, strlen (SUBJECT))) {
X	strcpy (linecopy, line);
X	line[0] = EOS;
X	fgets (line, 1023, fin);
X	if (!strncmp (line, LISTSERV, strlen (LISTSERV))) {
X	  sscanf (linecopy, "%s %s %s %s", junk, req, archive, nfile);
X	  if ((file[0] != EOS && strcmp (nfile, file)) ||
X	      (req[0] != EOS && strcmp (req, "GET")))
X	    goto cont;
X	  strcpy (file, nfile);
X	  subpart = nsubparts = 0;
X	  sscanf (strchr (linecopy, '('), "(%d/%d) [%d/%d]", &part, &nparts,
X		  &subpart, &nsubparts);
X	  if (!fout)
X	    if ((fout = fopen (file, "w")) == NULL)
X	      fprintf (stderr, "Cannot open output file %s\n", file),
X	      exit (1);
X	  if (parts_done < 0)	/* Initialize */
X	    parts_done = nparts;
X	  if ((part == prev_part + 1 && !subparts_done &&
X	       (subpart == 0 || subpart == 1)) ||
X	      (subparts_done && part == prev_part &&
X	       subpart == prev_subpart + 1)) {
X	    /* Append new part */
X	    if (part == 1 && !subparts_done)
X	      printf ("Combining parts into file %s...\n", file);
X	    if (!subparts_done)
X	      subparts_done = nsubparts;
X	    printf ("Extracting %s (%d/%d)", file, part, nparts);
X	    if (nsubparts)
X	      printf (" [%d/%d]", subpart, nsubparts);
X	    printf ("...");
X	    fflush (stdout);
X	    if (part == prev_part + 1)
X	      prev_part = part;
X	    if (subpart == prev_subpart + 1)
X	      prev_subpart = subpart,
X	      --nloops,
X	      --subparts_done;
X	    if (subpart == nsubparts)
X	      --parts_done,
X	      subpart = prev_subpart = subparts_done = nsubparts = 0;
X	    while (!feof (fin) && strncmp (line, CUT_HERE, strlen (CUT_HERE)))
X	      line[0] = EOS,
X	      fgets (line, 1023, fin);
X	    line[0] = EOS;
X	    fgets (line, 1023, fin);
X	    while (!feof (fin) && strncmp (line, CUT_HERE, strlen (CUT_HERE))) {
X	      fputs (line, fout);
X	      if (first_line && !strncmp (line, "begin", 5))
X		uuencoded = 1;
X	      line[0] = EOS;
X	      fgets (line, 1023, fin);
X	      first_line = 0;
X	    }
X	    puts ("");
X	  }
X	}
X      cont:
X	;
X      }
X    }
X    if (parts_done < 0)
X      fprintf (stderr, "No ListServer files found.\n"),
X      exit (3);
X    rewind (fin);
X    if ((++nloops) > nparts)
X      fprintf (stderr, "Parts missing; aborting.\n"),
X      exit (4);
X  }
X  fclose (fin);
X  fclose (fout);
X  if (uuencoded)
X    printf ("Uudecoding file %s...", file),
X    fflush (stdout),
X    sprintf (junk, "uudecode %s", file),
X    system (junk);
X  puts ("");
X  exit (0);
X}
*-*-END-of-util/combine.c-*-*
echo x - util/expn.c
sed 's/^X//' >util/expn.c <<'*-*-END-of-util/expn.c-*-*'
X/*-
X * $Id: expn.c,v 1.9 1992/05/31 03:43:02 dupuy Exp $
X *
X *	To compile with DNS support, cc -O -o expn expn.c -lresolv
X *	To compile without DNS support, cc -O -o expn -DNODOMAINS expn.c
X * 	To use, expn user@host
X *		exit codes: 0 = valid address
X *			    1 = system error (address may be valid)
X *			    2 = SMTP server error (address may be valid)
X *			    3 = invalid user
X *			    4 = invalid host
X *
X * This started out as mverify, by Jeff Beadles <jeff@quark.WV.TEK.COM>
X * with a couple of lines of autobounce.c by Pete Shipley <shipley@berkley.edu>
X *
X * I decided to enhance it for use in verifying mailing lists, requiring better
X * support for all the varieties of RFC-821 mailers out there in the swamps.
X * So I added support for MX records and real handling of RFC-821 result codes.
X *
X * If you think there's anything left to add, please send it to me, and I'll
X * see about adding it.
X *
X * Alexander Dupuy <dupuy@cs.columbia.edu>
X *
X */
X
X#ifdef BSD
X#include <strings.h>
X#else
X#include <string.h>
X#include <memory.h>
X#define index strchr
X#define bcopy(b1,b2,len) memcpy(b2,b1,len)
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netdb.h>
X#include <netinet/in.h>
X#ifndef NODOMAINS
X#include <arpa/nameser.h>
X#include <resolv.h>
X#endif
X#include <stdio.h>
X
X#define MAXMXHOSTS 20
X
X#define SYSERR 1
X#define SMTPERR 2
X#define BADUSER 3
X#define BADHOST 4
X
Xchar *fgets ();
Xchar *index ();
Xstruct hostent *gethostbyname ();
X
Xmain (argc, argv)
X    int argc;
X    char **argv;
X{
X    int smtpfd;
X    FILE *fin;				/* separate stdio streams to allow */
X    FILE *fout;				/* mixed reads and writes w/o seeks */
X    char buffer[2048];			/* stdio line buffer */
X
X    char *user;				/* pieces of argv */
X    char *orighost;
X    char *host;
X
X    char *command;			/* for expn/vrfy smtp commands */
X    int exitstatus;
X
X    struct hostent *hp;			/* inet networking */
X    struct servent *sp;
X    struct sockaddr_in server;
X
X    char *bp;				/* utility infielder */
X
X#ifndef NODOMAINS
X    int n;				/* utility index */
X
X    HEADER *dhp;			/* DNS reply header and fields*/
X    int ancount;
X    int qdcount;
X
X    union				/* DNS reply and pointers */
X    {
X	HEADER hdr;
X	u_char bytes[PACKETSZ];
X    }     answer;
X    u_char *cp;
X    u_char *eom;
X
X    char hostbuf[PACKETSZ];		/* hostnames buffer */
X    int buflen;
X
X    u_short pref;
X    u_short type;
X
X    char *hosts[MAXMXHOSTS];		/* arrays for multiple MX hosts */
X    u_short prefs[MAXMXHOSTS];
X    u_long bestpref;
X    int besthost;			/* indexes into arrays */
X    int i;
X#endif
X
X    if (argc != 2)
X    {
X	(void) fprintf (stderr, "Usage: %s user@host\n", argv[0]);
X	return (SYSERR);
X    }
X
X    user = argv[1];
X    if ((orighost = index (user, '@')) == 0)
X	host = "localhost";
X    else
X    {
X	*orighost = '\0';
X	orighost++;
X	host = orighost;
X    }
X
X    server.sin_family = AF_INET;
X
X    /*
X     * Get the smtp port using tcp.
X     */
X
X    sp = getservbyname ("smtp", "tcp");
X    server.sin_port = sp->s_port;
X    if (!server.sin_port)
X    {
X	(void) fprintf (stderr, "unknown service: smtp/tcp\n");
X	return (SYSERR);
X    }
X
X#ifdef NODOMAINS
X
X    /*
X     * Now get the information for the @host part of the address.
X     */
X
X    if ((hp = gethostbyname (host)) == 0)
X    {
X	(void) fprintf (stderr, "%s: unknown host\n", host);
X	return (BADHOST);
X    }
X
X#else
X
X    besthost = -1;			/* don't retry MX */
X
X    /*
X     * Check MX records for the @host part of the address.
X     */
X
X    n = res_search (host, C_IN, T_MX, answer.bytes, sizeof (answer));
X    if (n < 0)
X	goto punt;
X
X    /* find first satisfactory answer */
X
X    dhp = &answer.hdr;
X    cp = answer.bytes + sizeof (HEADER);
X    eom = answer.bytes + n;
X    for (qdcount = ntohs (dhp->qdcount); qdcount--; cp += n + QFIXEDSZ)
X	if ((n = dn_skipname (cp, eom)) < 0)
X	    goto punt;
X
X    /* copy MX hosts and preferences into arrays */
X
X    buflen = sizeof (hostbuf);
X    bp = hostbuf;
X    ancount = ntohs (dhp->ancount);
X    i = 0;
X    while (--ancount >= 0 && cp < eom && i < MAXMXHOSTS)
X    {
X	if ((n = dn_expand (&answer.hdr, eom, cp, bp, buflen)) < 0)
X	    break;
X	cp += n;
X	GETSHORT (type, cp);
X	cp += sizeof (u_short) + sizeof (u_long);
X	GETSHORT (n, cp);
X	if (type != T_MX)
X	{
X	    cp += n;
X	    continue;
X	}
X	GETSHORT (pref, cp);
X	if ((n = dn_expand (&answer.hdr, eom, cp, bp, buflen)) < 0)
X	    break;
X	cp += n;
X
X	prefs[i] = pref;
X	hosts[i] = bp;
X	i++;
X
X	n = strlen (bp) + 1;
X	bp += n;
X	buflen -= n;
X    }
X    hosts[i] = 0;			/* terminate host array */
X
X  nextmxhost:
X
X    i = 0;
X    n = 0;
X    bestpref = 65536;
X
X    while (hosts[i] && i < MAXMXHOSTS)
X    {
X	if (prefs[i] < 65535)		/* count untried MX hosts */
X	    n++;
X	if (prefs[i] < bestpref)
X	{
X	    bestpref = prefs[i];
X	    host = hosts[i];
X	    besthost = i;
X	}
X	i++;
X    }
X    if (n <= 1)				/* don't retry if this is last host */
X	besthost = -1;
X    
X  punt:
X
X    /*
X     * Now get the information for the @host part of the address.
X     */
X
X    if ((hp = gethostbyname (host)) == 0)
X    {
X	perror (host);
X	if (besthost >= 0)
X	{
X	    prefs[besthost] = 65535;
X	    goto nextmxhost;
X	}
X	if (h_errno == TRY_AGAIN)
X	    return (SYSERR);
X
X	return (BADHOST);
X    }
X
X#endif
X
X  reconnect:
X
X    (void) bcopy (hp->h_addr, (char *) &server.sin_addr, hp->h_length);
X
X    /*
X     * One socket please...
X     */
X
X    if ((smtpfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
X    {
X	perror ("socket");
X	return (SYSERR);
X    }
X
X    /*
X     * Connecting to the socket might make things go a little easier...  :-)
X     */
X
X    while (connect (smtpfd, (struct sockaddr *) &server, sizeof (server)) < 0)
X    {
X#ifdef h_addr
X	if (*++hp->h_addr_list)
X	{
X	    (void) close (smtpfd);
X	    goto reconnect;
X	}
X#endif
X	perror (host);
X	if (besthost >= 0)
X	{
X	    prefs[besthost] = 65535;
X	    (void) close (smtpfd);
X	    goto nextmxhost;
X	}
X	return (SYSERR);
X    }
X
X    /*
X     * Change them to streams 'cause I like 'em better.
X     */
X
X    fout = fdopen (smtpfd, "w");
X    fin = fdopen (smtpfd, "r");
X
X    /*
X     * The format of SMTP reply codes is a three digit number followed by
X     * either space or '-' minus.  The minus code indicates a multi-line
X     * response, so we keep reading lines until we see one with a space.
X     *
X     * Reply codes beginning with 2 are positive, and can be ignored
X     * (although 251 indicates a non-local user).
X     * Reply codes beginning with 4 indicate transient errors.
X     * Reply codes beginning with 5 indicate permanent errors
X     * (although 551 will return a forward-path for a non-local user).
X     *
X     * Note that RFC-1123 defines a new reply code 252 which indicates that
X     * the user cannot be verified, but the server will attempt forwarding.
X     * This is pretty much the same as 251 except there is less assurance that
X     * the address is valid.
X     */
X
X    /*
X     * Wait for the smtp mailer to answer.  It should greet us with a 220 code
X     */
X
X    while (fgets (buffer, sizeof (buffer) - 1, fin) != NULL)
X    {
X	if (buffer[3] == '-')
X	    continue;
X
X	if (!strncmp (buffer, "220 ", 4))
X	    break;
X	else				/* some kind of mailer error */
X	{
X	    (void) fputs (buffer, stderr);
X	    (void) putc ('\n', stderr);	/* fputs doesn't add a newline */
X	    exitstatus = SMTPERR;
X	    goto done;
X	}
X    }
X
X    exitstatus = 0;			/* let's be optimistic :-) */
X
X    if (orighost)
X	command = "EXPN %s@%s\r\n";	/* RFC-821 requires CRLF */
X    else
X	command = "EXPN %s\r\n";
X
X    /*
X     * Now that we have the mailer's attention, tell it to 'verify' the user.
X     * We have four ways of trying this, using EXPN or VRFY, and using @host
X     * or not.  We will try all of them (only two if no @host specified).
X     */
X
X    (void) fprintf (fout, command, user, orighost);
X    (void) fflush (fout);
X
X    /*
X     * Now just go into a loop reading responses from the mailer.
X     */
X
X    while (fgets (buffer, sizeof (buffer) - 1, fin) != NULL)
X    {
X	/*
X	 * Zap ^M from the lines.
X	 */
X	if ((bp = index (buffer, '\r')) != 0)
X	    *bp = '\0';
X
X	if (!strncmp (buffer, "250", 3))
X	{
X	    (void) puts (buffer + 4);	/* delete 250- code */
X	}
X	else if (!strncmp (buffer, "251", 3)) /* only given to VRFY */
X	{
X	    (void) puts (buffer);	/* unusual - leave 251 code in */
X	}
X	else if (!strncmp (buffer, "252", 3)) /* only given to VRFY */
X	{				/* reconstruct the user name we sent */
X	    if (orighost)
X		(void) printf ("<%s@%s>\n", user, orighost);
X	    else
X		(void) puts (user);
X
X	    (void) fputs (buffer, stderr);
X	    (void) putc ('\n', stderr);	/* print warning to stderr */
X	}
X	else if (!strncmp (buffer, "551", 3)) /* only given to VRFY */
X	{
X	    (void) puts (buffer);	/* unusual - leave 551 code in */
X
X	    (void) fputs (buffer, stderr);
X	    (void) putc ('\n', stderr);	/* print warning to stderr */
X	}
X	else if (buffer[0] == '5' && (buffer[1] == '0' || buffer[2] == '0'))
X	{				/* 50x syntax err; 550 list vs. user */
X	    /*
X	     * We try first with EXPN, then VRFY, since EXPN gives more info.
X	     * A server which accepts EXPN but not VRFY can give misleading
X	     * results for bad addresses.  The only server I have run across
X	     * (@lists.psi.com) accepts VRFY but not EXPN, so this is okay.
X	     * If some server does accept EXPN but not VRFY, then change this
X	     * to retry only for 50x codes.  RFC-821 says that a VRFY on a list
X	     * or an EXPN on a user is allowed to return 550, but I've never
X	     * seen that happen.
X	     */
X
X	    switch (exitstatus)
X	    {
X	      case 0:			/* first failure; try without @host */
X		if (orighost)		/* if we haven't already done so */
X		{
X		    command = "EXPN %s\r\n";
X		    exitstatus = 1;
X		    break;
X		}
X		/* fall through if !orighost */
X
X	      case 1:			/* EXPN doesn't work; try VRFY */
X		if (orighost)
X		{
X		    command = "VRFY %s@%s\r\n";
X		    exitstatus = 2;
X		    break;
X		}
X		/* fall through if !orighost */
X
X	      case 2:
X		command = "VRFY %s\r\n";
X		exitstatus = 3;
X		break;
X
X	      case 3:
X		if (buffer[1] == '5')	/* 55x implies bad username */
X		    exitstatus = BADUSER;
X		else
X		    exitstatus = SMTPERR;
X
X		(void) fputs (buffer, stderr);
X		(void) putc ('\n', stderr);
X		goto done;		/* we've had enough by now */
X	    }
X	    
X	    (void) fprintf (fout, command, user, orighost);
X	    (void) fflush (fout);
X	    continue;
X	}
X	else
X	{
X	    (void) fputs (buffer, stderr);	/* failed bigtime */
X	    (void) putc ('\n', stderr);	/* fputs doesn't add a newline */
X	}
X	
X	if (buffer[3] == ' ')		/* no more responses coming */
X	{
X	    if (buffer[0] == '2')
X		exitstatus = 0;		/* the last try worked */
X	    else if (buffer[0] == '5' && buffer[1] == '5')
X		exitstatus = BADUSER;	/* 55x implies bad username */
X	    else
X		exitstatus = SMTPERR;	/* SMTP error */
X	    break;
X	}
X    }
X
X  done:
X
X    /*
X     * Close the SMTP connection gracefully.
X     */
X
X    (void) fputs ("QUIT\r\n", fout);
X    (void) fflush (fout);
X    (void) fclose (fout);
X    (void) fclose (fin);
X
X    if (fflush (stdout))		/* in case we couldn't write output */
X	exitstatus = 1;
X
X    /*
X     * And leave this nice program.
X     */
X
X    return (exitstatus);
X}
X
*-*-END-of-util/expn.c-*-*
echo x - util/mmdfcatmail.c
sed 's/^X//' >util/mmdfcatmail.c <<'*-*-END-of-util/mmdfcatmail.c-*-*'
X/*
X *                              mmdfcatmail.c
X *
X *  Due to the behavior of MMDF (at least on SCO Unix), we can't use
X * listserv's /usr/server/catmail program within an aliased pipe.  MMDF strips
X * the first "From  " line in pipes, which listserv requires.  It would
X * probably be best if listserv always used the From: or Sender: rfc822 header.
X *
X * This program is a frontend to catmail.  It reads ahead in a mail steam and
X * picks the sender's address from the rfc822 header and assembles a fake
X * "From addr date" line and sends the whole mess to catmail.  All command line
X * arguments are passed directly to catmail.
X *
X *  To use this facility with MMDF, add lines such as this to your
X * /usr/mmdf/table/alias.list:
X *
X *  listserv: server|/usr/server/mmdfcatmail -r -f
X *  somelist: server|/usr/server/mmdfcatmail -L SOMELIST -f
X *
X * Note that your /usr/mmdf/mmdftailor file must have the "trusted" keyword
X * in the ALIAS entry for the alias.list file or pipes will not work:
X *
X *    ALIAS     table=lalias,   nobypass,       trusted 
X *
X * Here is the don't sue me copyright:
X *
X * Copyright 1993 Mark Diekhans (markd@grizzly.com)
X * Permission to use, copy, modify, and distribute this software and its
X * documentation for any purpose and without fee is hereby granted, provided
X * that the above copyright notice appear in all copies.  Mark Diekhans makes
X * no representations about the suitability of this software for any purpose.
X * It is provided "as is" without express or implied warranty.
X *
X * PORT: Ported by Tasos Kotsikonas (tasos@cs.bu.edu):
X * - CATMAIL is defined in ansi/start.h or nonansi/start.h
X * - used wait3() for certain systems instead of waitpid()
X * - if WAIT3_NEEDS_UNION is defined then use a union status variable
X * - STDIN_FILENO replaced by fileno(stdin)
X * - WIFEXITED and WEXITSTATUS adjusted according to the system
X */
X
X
X#include <stdio.h>
X#ifndef unknown_port
X# include <unistd.h>
X#endif
X#include <string.h>
X#include <ctype.h>
X#include <errno.h>
X#ifdef unknown_port
Xextern int errno;
X#endif
X#include <sys/types.h>
X#include <sys/wait.h>
X
X#include "defs.h"
X#include "struct.h"
X#include "global.h"
X#ifdef __STDC__
X# include "ansi/start.h"
X#else
X# include "nonansi/start.h"
X#endif
X#ifndef WAIT3_NEEDS_UNION
X# if defined (sequent) || defined (stardent) || defined (stellar) || \
X  defined (titan) || defined (unknown_port)
X#  ifdef WEXITSTATUS
X#   undef WEXITSTATUS
X#  endif
X#  ifdef WIFEXITED
X#   undef WIFEXITED
X#  endif
X# endif
X#endif
X
X#ifndef WEXITSTATUS
X# define  WEXITSTATUS(stat)     ((int)(((stat)>>8)&0377))
X#endif
X# ifndef WIFEXITED
X#  define WIFEXITED(stat)       (((int)((stat)&0377))==0)
X# endif
X
XBOOLEAN tty_echo = FALSE; /* -e option off */
XFILE *report      = NULL; /* Progress report to the administrator */
X
Xextern void extract_address (char *);
X
X/*
X * Maximum number of lines to read looking for "From:" or "Sender:".
X */
X#define MAX_HEADER_LINES  2048
X
X/*
X * Type used to save header fields in memory.  Also structure used to
X * keep a list of these header lines.
X */
Xtypedef struct strEntry_t {
X    struct strEntry_t *next;
X    char               line [1];  /* MUST BE LAST FIELD. */
X} strEntry_t;
X
Xtypedef struct {
X    strEntry_t  *head;
X    strEntry_t  *tail;
X} strList_t;
X
X/*
X * Save a line away in a dynamically allocated structure and added that
X * structure to a list.
X */
X
Xvoid
XSaveLine (const char  *line,
X          strList_t   *list)
X{
X    strEntry_t *entry;
X
X    entry = (strEntry_t *) malloc (sizeof (strEntry_t) + (strlen (line)));
X    strcpy (entry->line, line);
X    entry->next = NULL;
X
X    if (list->tail == NULL) {
X        list->head = entry;
X    } else {
X        list->tail->next = entry;
X    }
X    list->tail = entry;
X}
X
X/*
X * Start the catmail process.  
X */
Xpid_t
XStartCatmail (const char **argv,
X              FILE       **toPipe)
X{
X    int    pipefds [2];
X    pid_t  pid;
X
X    *toPipe = NULL;
X
X    if (pipe (pipefds) == 0) {
X        *toPipe = fdopen (pipefds [1], "w");
X    }
X    if (*toPipe == NULL) {
X        fprintf (stderr, "open of pipe failed: %s", strerror (errno));
X        exit (1);
X    }
X
X    pid = fork ();
X    if (pid < 0) {
X        fprintf (stderr, "fork failed: %s", strerror (errno));
X        exit (1);
X    }
X    if (pid > 0) {
X        close (pipefds [0]);
X        return pid;
X    }
X
X    close (pipefds [1]);
X    close (fileno (stdin));
X    if (dup2 (pipefds [0], fileno (stdin)) < 0) {
X        fprintf (stderr, "dup to stdin failed: %s", strerror (errno));
X        exit (1);
X    }
X 
X    argv [0] = strrchr (CATMAIL, '/');
X    if (argv [0] == NULL)
X        argv [0] = CATMAIL;
X    execv (CATMAIL, argv);
X    
X    fprintf (stderr, "exec of %s failed: %s", CATMAIL, strerror (errno));
X    exit (1);
X}
X
X/*
X * Do actual work of copying stdin catmail process, filling in the missing
X * "From address Date" line.
X */
Xmain (int          argc,
X      const char **argv)
X{
X    strList_t    headerList;
X    strEntry_t  *entry;
X    char         line [MAX_LINE + 1];
X    char         fromAddress [MAX_LINE];
X    char         date [MAX_LINE];
X    int          linesRead = 0;
X#ifdef WAIT3_NEEDS_UNION
X    union wait	 waitStat;
X#else
X    int		 waitStat;
X#endif
X    pid_t        pid;
X    FILE        *toPipe;
X
X    headerList.head = NULL;
X    headerList.tail = NULL;
X    fromAddress [0] = '\0';
X    date [0] = '\0';
X
X    /*
X     * Search for the minimum headers we need and save away lines for later
X     * output.
X     */
X    while (! ((fromAddress [0] != '\0') && (date [0] != '\0'))) {
X        if (fgets (line, MAX_LINE, stdin) == NULL)
X            break;
X
X        SaveLine (line, &headerList);
X
X        if (strncmp (line, "From:", 5) == 0) {
X            strcpy (fromAddress, line + 5); 
X            extract_address (fromAddress);
X            continue;
X        }
X        if (strncmp (line, "Sender:", 6) == 0) {
X            strcpy (fromAddress, line + 6); 
X            extract_address (fromAddress);
X            continue;
X        }
X        if (strncmp (line, "Date:", 5) == 0) {
X            strcpy (date, line + 6); 
X            continue;
X        }
X        linesRead++;
X        if (linesRead > MAX_HEADER_LINES)
X            break;
X    }
X    if (fromAddress [0] == '\0') {
X        fprintf (stderr, "No \"From:\" or \"Sender:\" header found\n");
X        exit (1);
X    }
X    if (date [0] == '\0') {
X        fprintf (stderr, "No \"date:\" header found\n");
X        exit (1);
X    }
X
X    pid = StartCatmail (argv, &toPipe);
X
X    /*
X     * Output our madeup from line. (date has "\n").
X     */
X    fprintf (toPipe, "From %s %s", fromAddress, date);
X
X    entry = headerList.head;
X    while (entry != NULL) {
X        fputs (entry->line, toPipe);
X        entry = entry->next;
X    }
X
X    while (fgets (line, MAX_LINE, stdin) != NULL)
X        fputs (line, toPipe);
X
X    fclose (toPipe);
X
X#if defined (sequent) || defined (stardent) || defined (stellar) || \
X defined (titan) || defined (unknown_port)
X    do {
X    } while ((pid = wait3 (&waitStat, WNOHANG, NULL)));
X    if (pid < 0) {
X	fprintf (stderr, "wait3 failed: %s", strerror (errno));
X        exit (1);
X    }
X#else
X    if (waitpid (pid, &waitStat, 0) < 0) {
X        fprintf (stderr, "waitpid failed: %s", strerror (errno));
X        exit (1);
X    }
X#endif
X
X    if (WIFEXITED (waitStat))
X        exit (WEXITSTATUS (waitStat));
X
X    fprintf (stderr, "%s terminated for unknown reasons\n", CATMAIL);
X    exit (1);
X
X}
X
X/*
X * The library needs this guy.
X */
Xint
Xgexit (int exitcode)
X{
X    exit (exitcode);
X}
X
*-*-END-of-util/mmdfcatmail.c-*-*
echo x - util/use_From:_address.sh
sed 's/^X//' >util/use_From:_address.sh <<'*-*-END-of-util/use_From:_address.sh-*-*'
X#!/bin/sh
X# shar file containing a shell script (Catmail) and a C program that will
X# use the From: address for user identification instead (UNIX ListServer uses
X# the "From " address). See the README file for Catmail usage.
X#
Xecho x - README
Xsed 's/^X//' >README <<'*-*-END-of-README-*-*'
XXContents:
XX
XXREADME		- this file
XXCatmail		- use in place of catmail in your aliases file (e.g.
XX		  listserv:	"|/usr/server/Catmail -r -f")
XX		  Should be owned by root; change paths as necessary
XXaddress.c	- used by Catmail; no special compilation options; should be
XX		  executable by root
X*-*-END-of-README-*-*
Xecho x - Catmail
Xsed 's/^X//' >Catmail <<'*-*-END-of-Catmail-*-*'
XX#! /bin/sh
XXno_address=0
XXcat > /tmp/In.Mail.$$
XXchown server /tmp/In.Mail.$$
XX/usr/server/address "`grep '^From: ' /tmp/In.Mail.$$`" > /tmp/Mail.$$
XX#grep '^From: ' /usr/server/In.Mail.$$ | awk '{ printf "From " $2 " "; exit }' > /usr/server/Mail.$$
XXif [ $? -gt 0 ]; then
XX  no_address=1
XXfi
XXdate >> /tmp/Mail.$$
XXcat /tmp/In.Mail.$$ >> /tmp/Mail.$$
XXif [ $no_address -gt 0 ]; then
XX  MANAGER=`grep "^manager " /usr/server/config | awk '{ print $2 }'`
XX  cat /tmp/Mail.$$ | /bin/rmail $MANAGER
XXelse
XX  cat /tmp/Mail.$$ | /usr/server/catmail $*
XXfi
XXrm /tmp/In.Mail.$$ /tmp/Mail.$$
X*-*-END-of-Catmail-*-*
Xecho x - address.c
Xsed 's/^X//' >address.c <<'*-*-END-of-address.c-*-*'
XX#include <stdio.h>
XX#include <ctype.h>
XX#include <string.h>
XX
XX#define EOS	'\0'
XX#define FROM	"From: "
XX
XX/*
XX  Given an RFC 822 From: line address, strip comments etc. and extract
XX  the actual address.
XX*/
XX
XXvoid extract_address (char *s)
XX{
XX  char *p;
XX
XX  if (p = strchr (s, '<'))
XX    sprintf (s, "%s", p + 1); /* Remove '<' */
XX  if (*s == '|' || *s == ':')
XX    *s = EOS;  /* Protect against trojans */
XX  if (p = strchr (s, '>'))
XX    *p = EOS;  /* Remove '>' */
XX  if (p = strchr (s, '('))
XX    *p = EOS; /* Remove comments */
XX  else {  /* Get to the end of the address */
XX    while (*s != EOS && !isspace (*s))
XX      ++s;
XX    *s = EOS;
XX  }
XX}
XX
XXmain (int argc, char **argv)
XX{
XX  if (argc < 2 || strncmp (argv[1], FROM, strlen (FROM)))
XX    exit (1);
XX  sprintf (argv[1], "%s", argv[1] + strlen (FROM));
XX  extract_address (argv[1]);
XX  printf ("From %s ", argv[1]);
XX  exit (0);
XX}
X*-*-END-of-address.c-*-*
Xexit
*-*-END-of-util/use_From:_address.sh-*-*
echo x - util/which.sh
sed 's/^X//' >util/which.sh <<'*-*-END-of-util/which.sh-*-*'
X#!/bin/sh
X#
X# Source to the UCB "which" utility; for use by the "systest" script
X#
XPATH=/bin:/usr/bin:/usr/ucb
Xecho Extracting 'which6.c'
Xsed 's/^X//' > 'which6.c' << '+ END-OF-FILE ''which6.c'
XX/*
XX * which [-i] [-a] [--] [<command>]
XX * alias which alias !\$ \| /usr/local/bin/which -i !\*
XX * alias which 'eval alias \$$# | /usr/local/bin/which -i ${1+"$@"}'
XX * which()
XX * {
XX *	eval last=\"\$$#\"
XX *	set | sed -n "/^$last(){$/,/^}$/p" |
XX *		/usr/local/bin/which -i ${1+"$@"}
XX * }
XX *
XX * Author: Maarten Litmaath @ VU University Amsterdam (maart@cs.vu.nl)
XX *
XX * First change:
XX *	Emile LeBlanc (leblanc%math.Berkeley.EDU@ucbvax.berkeley.edu) notes
XX *	the access() system call considering everything executable for
XX *	root (!), so we give root a special treatment.  :-(
XX *	`which', `which -i' and `which -a' with no further arguments now
XX *	return the PATH environment variable, split up into its components.
XX *	The aliases defined above are slightly different from the previous
XX *	version - now it's the shell who's doing the alias checking.
XX * Second change:
XX *	Added support for shell functions and multiline aliases, added the
XX *	`--' option, changed the source style.
XX * Third change:
XX *	To hell with access()!
XX *	Now stat() is used to give the right answer even if the effective
XX *	uid (gid) differs from the real uid (gid).
XX *	We can't use setuid(geteuid()), because that's nonportable.  :-(
XX * Fourth change:
XX *	Jim Meyering <meyering@cs.utexas.edu> notes convert() will clobber
XX *	the stack if the PATH is longer than BUF_SIZE - 1 characters.
XX *	I've changed convert() altogether to return a path vector (cf. argv),
XX *	whose components are the respective directories in the PATH.
XX *	Furthermore in printing the PATH there are no trailing colons anymore.
XX * Fifth change:
XX *	I've added support for multiple groups (see getgroups(2) on BSD-
XX *	derivatives).
XX *	Thanks to John M. Sellens <jmsellens@watdragon.uwaterloo.ca> (and
XX *	Andy at the same site).
XX *	Furthermore, if a matching executable is found in an unreadable
XX *	directory, beside the warning on stderr the name is now printed on
XX *	stdout as well.
XX */
XX
XX#ifdef	MULTIPLE_GROUPS
XX#include	<sys/param.h>
XX#else
XX#include	<sys/types.h>
XX#endif	/* MULTIPLE_GROUPS */
XX#include	<sys/stat.h>
XX#include	<stdio.h>
XX
XX#define		BUF_SIZE	512
XX#define		M_USR		0700
XX#define		M_GRP		0070
XX#define		M_OTH		0007
XX#define		X_ALL		0111
XX#define		R_ALL		0444
XX
XXchar	Version[] =
XX	"@(#)which 6.0 91/02/02 Maarten Litmaath @ VU Informatika Amsterdam";
XXchar	*Prog;
XX
XX
XXvoid	usage()
XX{
XX	fprintf(stderr, "Usage: %s [-i] [-a] [--] [<command>]\n", Prog);
XX	exit(1);
XX}
XX
XX
XXmain(argc, argv) 
XXint	argc;
XXregister char	**argv;
XX{
XX	register char	*path, *s, **pathv, **p;
XX	char	*strcpy(), *getenv(), *fgets(), buf[BUF_SIZE], **convert();
XX	int	all = 0, inter = 0, stop = 0, found = 0, uid, gid, mask,
XX		xmask, rmask;
XX	struct	stat	st;
XX	void	usage();
XX
XX
XX	Prog = *argv++;
XX	--argc;
XX
XX	while (!stop && (s = *argv) && (*s == '-')) {
XX		++argv;
XX		--argc;
XX		++s;
XX		while (*s)
XX			switch (*s++) {
XX			case 'a':
XX				all = 1;
XX				break;
XX			case 'i':
XX				inter = 1;
XX				break;
XX			case '-':
XX				stop = 1;
XX				break;
XX			default:
XX				usage();
XX			}
XX	}
XX
XX	if (argc > 1)
XX		usage();
XX
XX	if (inter && *argv) {
XX		while (fgets(buf, sizeof buf, stdin)) {
XX			if (!found) {
XX				printf("%s", *argv);
XX				found = 1;
XX			}
XX			printf("\t%s", buf);
XX		}
XX		if (found && !all)
XX			exit(0);
XX	}
XX
XX	if (!(path = getenv("PATH"))) {
XX		fprintf(stderr, "%s: no PATH in environment!\n", Prog);
XX		exit(1);
XX	}
XX
XX	if (!*path)
XX		path = ".";		/* another dubious convention */
XX
XX	pathv = convert(path);		/* convert path string to vector */
XX
XX	if (!*argv) {			/* print path if no more arguments */
XX		while (*pathv)
XX			puts(*pathv++);
XX		exit(0);
XX	}
XX
XX	uid = geteuid();
XX	gid = getegid();
XX	if (uid == 0) {
XX		xmask = X_ALL;
XX		rmask = R_ALL;
XX	}
XX
XX	for (p = pathv; path = *p++; ) {	/* try every component */
XX		s = buf;
XX		while (*s++ = *path++)
XX			;
XX		(void) strcpy(s, *argv);
XX		*--s = '/';
XX
XX		if (stat(buf, &st) != 0 || (st.st_mode & S_IFMT) != S_IFREG)
XX			continue;
XX
XX		/* file exists and is regular */
XX
XX		if (uid != 0) {
XX			mask = st.st_uid == uid ? M_USR :
XX				st.st_gid == gid ? M_GRP :
XX#ifdef	MULTIPLE_GROUPS
XX				groups_member(st.st_gid) ? M_GRP :
XX#endif	/* MULTIPLE_GROUPS */
XX				M_OTH;
XX			xmask = X_ALL & mask;
XX			rmask = R_ALL & mask;
XX		}
XX
XX		if (!(st.st_mode & xmask))
XX			continue;
XX
XX		/* file is executable */
XX
XX		*s = 0;
XX		if (stat(buf, &st) != 0) {
XX			perror(buf);
XX			continue;
XX		}
XX
XX		/* warn user if the directory is unreadable */
XX
XX		if (!(st.st_mode & rmask))
XX			fprintf(stderr,
XX				"%s: %s found in unreadable directory `%s'!\n",
XX				Prog, *argv, buf);
XX
XX		*s = '/';
XX		puts(buf);
XX		if (!all)
XX			exit(0);
XX		found = 1;
XX	}
XX
XX	if (found)
XX		exit(0);
XX
XX	fprintf(stderr, "%s not found in:\n", *argv);
XX	while (*pathv)
XX		fprintf(stderr, "%s\n", *pathv++);
XX	exit(1);
XX	/* NOTREACHED */
XX}
XX
XX
XXchar	**convert(path)
XXchar	*path;
XX{
XX	register char	*s, c;
XX	register int	pathc;		/* the length of the path vector */
XX	char	**v, **pathv, *malloc();
XX
XX	for (s = path, pathc = 2; c = *s++; )
XX		if (c == ':')
XX			++pathc;
XX
XX	if (!(pathv = (char **) malloc(pathc * sizeof(char *)))) {
XX		perror("malloc");
XX		exit(1);
XX	}
XX
XX	for (s = path, v = pathv; (c = *s) != '\0'; ) {
XX		if (c == ':') {
XX			/*
XX			 * This colon is spurious.  According to some
XX			 * dubious convention it is made equivalent to a dot.
XX			 */
XX			*v++ = ".";
XX			if (*++s == '\0')
XX				*v++ = ".";
XX				/*
XX				 * The PATH ended in a spurious colon.
XX				 * To be consistent we add another dot
XX				 * to the path vector.  One day you'll
XX				 * be glad we did.
XX				 */
XX		} else {
XX			*v++ = s;
XX			while ((c = *++s) != '\0')
XX				if (c == ':') {
XX					*s++ = '\0';
XX					if (*s == '\0')
XX						*v++ = ".";
XX						/*
XX						 * The PATH ended in a
XX						 * (spurious) colon, so
XX						 * add dot to the vector.
XX						 */
XX					break;
XX				}
XX		}
XX	}
XX
XX	*v = 0;		/* Signal the end of the path vector. */
XX
XX	return pathv;
XX}
XX
XX
XX#ifdef	MULTIPLE_GROUPS
XXint	groups_member(gid)
XXint	gid;
XX{
XX	register
XX	int	*p, ngroups;
XX	int	groups[NGROUPS];
XX
XX	if ((ngroups = getgroups(NGROUPS, groups)) < 0)
XX		return 0;
XX	p = groups;
XX        while (--ngroups >= 0)
XX		if (*p++ == gid)
XX			return 1;
XX	return 0;
XX}
XX#endif	/* MULTIPLE_GROUPS */
X+ END-OF-FILE which6.c
Xchmod 'u=rw,g=r,o=r' 'which6.c'
Xset `wc -c 'which6.c'`
Xcount=$1
Xcase $count in
X5970)	:;;
X*)	echo 'Bad character count in ''which6.c' >&2
X		echo 'Count should be 5970' >&2
Xesac
Xecho Extracting 'which.1'
Xsed 's/^X//' > 'which.1' << '+ END-OF-FILE ''which.1'
XX.TH WHICH 1 Apr\ 04\ 1990
XX.SH NAME
XXwhich \- give alias, function or path expansion of command
XX.SH SYNOPSIS
XX.B which
XX[
XX.B \-i
XX] [
XX.B \-a
XX] [
XX.B \-\-
XX] [
XX.I command
XX]
XX.SH DESCRIPTION
XX.I Which
XXprovides the user with the full expansion of the
XX.I command
XXargument, be it either an \fIalias\fR, a \fIshell function\fR
XXor an executable file (default). To enable search for
XX.I aliases
XXand \fIshell functions\fR
XXthe user should supply the `\fI\-i\fR'
XX(= interactive) flag. In that case
XX.I which
XXexpects as standard input the expansion of the \fIalias\fR
XXor \fIshell function\fR.
XXIf the standard input is empty or the `\fI\-i\fR' flag has not been
XXgiven, \fIwhich\fR will try to locate \fIcommand\fR
XXin the user's \fBPATH\fR.
XXThe interactive mode is easily used by setting an
XX.I alias
XXlike the following:
XX.ft B
XX.nf
XX
XX	alias	which	alias !\\$ \\| /usr/local/bin/which \-i !\\*
XX
XX.fi
XX.ft R
XXin \fIcsh\fR, or
XX.ft B
XX.nf
XX
XX	alias	which	eval alias '\\"\\$$#\\" |' \\
XX			/usr/local/bin/which \-i '${1+"$@"}'
XX
XX.fi
XX.ft R
XXin shells which are supersets of
XX.I sh
XXand which know \fIaliases\fR. If your shell has \fIshell functions\fR, you
XXcan use the following function:
XX.ft B
XX.nf
XX
XX	which()
XX	{
XX		eval last=\\"\\$$#\\"
XX		set | sed \-n "/^$last(){$/,/^}$/p" |
XX			/usr/local/bin/which \-i ${1+"$@"}
XX	}
XX
XX.fi
XX.ft R
XXIf the `\fI\-a\fR' (= all) flag is given,
XX.I which
XXwill not stop after the first `match', but search for all occurrences of
XX.I command
XXin the user's
XX.B PATH.
XXThe `\fI\-\-\fR'
XXflag can be used to end the list of options: the next argument (if present)
XXwill be taken as \fIcommand\fR, even if it starts with a `\-'.
XX\fIWhich [\-i] [\-a] [\-\-]\fR
XXwithout further arguments prints the user's
XX.B PATH
XXbroken up into its components,
XXone per line.
XX.PP
XXThis new version of the
XX.I which
XXcommand is not a
XX.I csh
XXscript.
XXBeing an executable it is much faster, and not sourcing 
XX.I .cshrc
XXit gives a true picture of one's
XX.I aliases.
XXFurthermore it will give the correct answers even if:
XX.IP \-
XXthe \fIeffective uid\fR
XX(\fIgid\fR) differs from the \fIreal\fR uid (gid)
XX.IP \-
XXthe effective uid is 0 (\fIroot\fR).
XX.SH EXAMPLE
XX.ft B
XX.nf
XX% alias
XXwhich	alias !$ | /usr/local/bin/which \-i !*
XX% which which
XXwhich	alias !$ | /usr/local/bin/which \-i !*
XX% which \-a which
XXwhich	alias !$ | /usr/local/bin/which \-i !*
XX/usr/local/bin/which
XX/usr/ucb/which
XX%
XX.fi
XX.ft R
XX.SH AUTHOR
XXMaarten Litmaath @ VU Informatika Amsterdam
X+ END-OF-FILE which.1
Xchmod 'u=rw,g=r,o=r' 'which.1'
Xset `wc -c 'which.1'`
Xcount=$1
Xcase $count in
X2384)	:;;
X*)	echo 'Bad character count in ''which.1' >&2
X		echo 'Count should be 2384' >&2
Xesac
Xecho Extracting 'Makefile'
Xsed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
XX# Makefile for /usr/local/bin/which
XX# If your operating system does not support multiple groups (see getgroups(2)
XX# on BSD-derivatives), comment out the next line.
XXMULTIPLE_GROUPS=-DMULTIPLE_GROUPS
XX
XXwhich:		which6.c
XX		cc -O $(MULTIPLE_GROUPS) -o which which6.c
XX
XXinstall:	which doc
XX		/bin/mv -f which /usr/local/bin
XX		/bin/mv -f which.man /usr/local/man/cat1/which.1
XX		/bin/cp which.1 /usr/local/man/man1
XX
XXdoc:
XX		nroff -man which.1 > which.man
X+ END-OF-FILE Makefile
Xchmod 'u=rw,g=r,o=r' 'Makefile'
Xset `wc -c 'Makefile'`
Xcount=$1
Xcase $count in
X443)	:;;
X*)	echo 'Bad character count in ''Makefile' >&2
X		echo 'Count should be 443' >&2
Xesac
Xexit 0
X
Xexit 0 # Just in case...
X-- 
XKent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
XSterling Software, IMD           UUCP:     uunet!sparky!kent
XPhone:    (402) 291-8300         FAX:      (402) 291-4362
XPlease send comp.sources.misc-related mail to kent@uunet.uu.net.
X
*-*-END-of-util/which.sh-*-*
echo x - makefile
sed 's/^X//' >makefile <<'*-*-END-of-makefile-*-*'
XTARGETS		= listserv list serverd start tlock farch pqueue catmail iul \
X		  rev fwin
Xall:
X	@./setup
X
Xinstall:
X	@cd src; make install
X
Xclean:
X	rm -rf $(TARGETS)
X	(cd src; make -i clean)
X	(cd gateway; make -i clean)
X
Xlistserv:
X	(cd src; make $@)
X
Xlist:
X	cd src; make $@
X
Xserverd:
X	(cd src; make $@)
X
Xstart:
X	(cd src; make $@)
X
Xtlock:
X	(cd src; make $@)
X
Xfarch:
X	(cd src; make $@)
X
Xpqueue:
X	(cd src; make $@)
X
Xcatmail:
X	(cd src; make $@)
X
Xiul:
X	(cd src; make $@)
X
Xrev:
X	(cd src; make $@)
X
Xfwin:
X	(cd src; make $@)
*-*-END-of-makefile-*-*
echo x - welcome.live
sed 's/^X//' >welcome.live <<'*-*-END-of-welcome.live-*-*'
X
XREMINDER:
Xquit: end the session at any time.
X? or privileges: get a listing of all valid requests you may issue.
Xtimeleft: get the remaining time of the connection.
X
XLOG IN PRIVILEGES:
XThe set of requests you may issue during this session depends on the privileges
Xassigned by the system. You may log in as a:
X- Subscriber: provide your email address and password as subscribed to one of
X  the local lists.
X- Owner: simply provide your email address and your list's password.
X- Manager: provide your email address and the system's password.
X- Other: simply hit return at the prompt below.
X
*-*-END-of-welcome.live-*-*
echo x - systest
sed 's/^X//' >systest <<'*-*-END-of-systest-*-*'
X#!/bin/sh
X# This script will test your system's suitability to run UNIX ListServer
X# Follow the suggestions made.
X
Xdirnam()
X{
X  base=`basename $1`
X  echo $1 | sed "s/\/$base//g"
X}
X
Xtrap 'rm -f c.c c.o a.out core > /dev/null 2>&1; exit 1' 1 2 3 6
Xports="SUN IBM SGI DEC HP Convex Stardent KPC NeXT SCO Apollo Sequent DG i860 OSF i386"
XCC="cc"
X
Xecho \	Testing your system\'s suitability to run UNIX ListServer 6.0 ...
Xecho
X
Xwhich cat > /dev/null 2>&1
Xif [ $? -ne 0 ]; then
X  echo You need to have UCB\'s \"which\" utility to be able to run this script
X  exit 1
Xfi
X
X# Determine how echo suppresses new-line
Xecho "a\c" > /tmp/echo
Xif [ "`grep c /tmp/echo`" = "a\c" ]; then
X  n='-n'
X  c=''
Xelse
X  n=''
X  c='\c'
Xfi
Xrm /tmp/echo
X
Xecho NOTICE: Make sure you redefine the CC variable in this script to use the same
Xecho compiler and flags you will be using to compile the system.
Xecho
Xecho The system has been ported to the following UNIXes:
Xecho $ports
Xcat > c.c << EOF
X#include <stdio.h>
Xmain ()
X{
X  printf (
X#ifdef sequent
X  "Sequent\n"
X#elif defined (__osf__)
X  "OSF\n"
X#elif defined (ultrix)
X  "DEC\n"
X#elif defined (__NeXT__)
X  "NeXT\n"
X#elif defined (apollo)
X  "Apollo\n"
X#elif defined (__convex__)
X  "Convex\n"
X#elif defined (sun)
X  "SUN\n"
X#elif defined (hpux) || defined (__hpux)
X  "HP\n"
X#elif defined (sgi) || defined (__sgi)
X  "SGI\n"
X#elif defined (_AIX)
X  "IBM Risc\n"
X#elif defined (stellar)
X  "Stellar GS (Stardent)\n"
X#elif defined (titan)
X  "Titan (Stardent/KPC)\n"
X#elif defined (xenix)
X  "Xenix\n"
X#elif defined (M_XENIX) || defined (M_UNIX)
X  "SCO\n"
X#elif defined (__DGUX__)
X  "Data General\n"
X#elif defined (i860)
X  "i860 architecture\n"
X#elif defined (i386)
X  "i386 architecture\n"
X#else
X  "\n"
X#endif
X  );
X}
XEOF
X$CC c.c > /dev/null 2>&1
Xif [ ! -f a.out ]; then
X  cat > c.c << EOF
X#include <stdio.h>
Xmain ()
X{
X  printf (
X#if defined (hpux) || defined (__hpux)
X  "HP\n"
X#else
X#  if defined (__osf__)
X  "OSF\n"
X#  else
X#    if defined (sequent)
X  "Sequent\n"
X#    else
X#      if defined (ultrix)
X  "DEC\n"
X#      else
X#        if defined (__NeXT__)
X  "NeXT\n"
X#        else
X#          if defined (apollo)
X  "Apollo\n"
X#          else
X#            if defined (__convex__)
X  "Convex\n"
X#            else
X#              if defined (sun)
X  "SUN\n"
X#              else
X#                if defined (sgi) || defined (__sgi)
X  "SGI\n"
X#                else
X#                  if defined (_AIX)
X  "IBM Risc\n"
X#                  else
X#                    if defined (stellar)
X  "Stellar GS (Stardent)\n"
X#                    else
X#                      if defined (titan)
X  "Titan (Stardent/KPC)\n"
X#                      else
X#                        if defined (xenix)
X  "Xenix\n"
X#                        else
X#                          if defined (M_XENIX) || defined (M_UNIX)
X  "SCO\n"
X#                          else
X#                            if defined (__DGUX__)
X  "Data General\n"
X#                            else
X#                              if defined (i860)
X  "i860 architecture\n"
X#                              else
X#				 if defined (i386)
X  "i386 architecture\n"
X#				 else
X  "\n"
X#				 endif
X#			       endif
X#                            endif
X#                          endif
X#                        endif
X#                      endif
X#                    endif
X#                  endif
X#                endif
X#              endif
X#            endif
X#          endif
X#        endif
X#      endif
X#    endif
X#  endif
X#endif
X  );
X}
XEOF
X $CC c.c > /dev/null 2>&1
Xfi
Xhost=`./a.out`
Xif [ "$host" = "" ]; then
X  echo $n "Your system does not seem to be listed in there; is this true [y]? $c"
X  read x
X  if [ "$x" = "n" ] || [ "$x" = "N" ]; then
X    known_port=' '
X  else
X    echo '*** Compile with -Dunknown_port'
X  fi
Xelse
X  echo Good, the system has already been ported to your $host.
X  known_port=' '
Xfi
Xecho
X
Xecho --- Looking for required UNIX utilities:
Xecho echo: found
Xecho $n "cut: $c"
Xif [ "`which cut | grep '^/'`" = "" ]; then
X  missing=cut\ 
X  echo not found: replace cut with awk in .stats, src/listserv.c, src/serverd.c
X  workaround=cut\ 
Xelse
X  echo found
Xfi
Xecho $n "paste: $c"
Xif [ "`which paste | grep '^/'`" = "" ]; then
X  missing=${missing:-}paste\ 
X  echo not found: statistics requests will look wrong
X  should_have=paste\ 
Xelse
X  echo found
Xfi
Xecho $n "wc: $c"
Xif [ "`which wc | grep '^/'`" = "" ]; then
X  missing=${missing:-}wc\ 
X  echo not found: behavior undefined
X  have_to_have=wc\ 
Xelse
X  echo found
Xfi
Xecho $n "cat: $c"
Xif [ "`which cat | grep '^/'`" = "" ]; then
X  missing=${missing:-}cat\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}cat\ 
Xelse
X  echo found
Xfi
Xecho $n "tr: $c"
Xif [ "`which tr | grep '^/'`" = "" ]; then
X  missing=${missing:-}tr\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}tr\ 
Xelse
X  echo found
Xfi
Xecho $n "awk: $c"
Xif [ "`which awk | grep '^/'`" = "" ]; then
X  missing=${missing:-}awk\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}awk\ 
Xelse
X  echo found
Xfi
Xecho $n "mv: $c"
Xif [ "`which mv | grep '^/'`" = "" ]; then
X  missing=${missing:-}mv\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}mv\ 
Xelse
X  echo found
Xfi
Xecho $n "cp: $c"
Xif [ "`which cp | grep '^/'`" = "" ]; then
X  missing=${missing:-}cp\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}cp\ 
Xelse
X  echo found
Xfi
Xecho $n "rm: $c"
Xif [ "`which rm | grep '^/'`" = "" ]; then
X  missing=${missing:-}rm\ 
X  echo not found: behavior undefined
X  have_to_have=${have_to_have:-}rm\ 
Xelse
X  echo found
Xfi
Xecho $n "grep -i: $c"
Xgrep -i junk < /dev/null > /dev/null 2>&1
Xif [ $? -eq 2 ]; then
X  missing=${missing:-}grep -i\ 
X  echo: the -i flag is not supported\; check the pmgrep utility on cs.bu.edu
X  have_to_have=${have_to_have:-}grep -i\ 
Xelse
X  echo works OK
Xfi
Xecho $n "uuencode: $c"
Xif [ "`which uuencode | grep '^/'`" = "" ]; then
X  missing=${missing:-}uuencode\ 
X  echo not found: cannot archive binary files
X  should_have=${should_have:-}uuencode\ 
Xelse
X  echo found
Xfi
Xecho $n "compress: $c"
Xif [ "`which compress | grep '^/'`" = "" ]; then
X  missing=${missing:-}compress\ 
X  echo not found: archives will not be uncompressed
X  should_have=${should_have:-}compress\ 
Xelse
X  echo found
Xfi
Xecho $n "zcat: $c"
Xif [ "`which zcat | grep '^/'`" = "" ]; then
X  missing=${missing:-}zcat\ 
X  echo not found: cannot archive binary files
X  should_have=${should_have:-}zcat\ 
Xelse
X  echo found
Xfi
Xecho $n "uncompress: $c"
Xif [ "`which uncompress | grep '^/'`" = "" ]; then
X  missing=${missing:-}uncompress\ 
X  echo not found: archives will remain uncompressed
X  should_have=${should_have:-}uncompress\ 
Xelse
X  echo found
Xfi
Xecho $n "tar: $c"
Xif [ "`which tar | grep '^/'`" = "" ]; then
X  missing=${missing:-}tar\ 
X  echo not found: cannot archive into tar archives
X  should_have=${should_have:-}tar\ 
Xelse
X  echo found
Xfi
Xecho $n "egrep: $c"
Xif [ "`which egrep | grep '^/'`" = "" ]; then
X  missing=${missing:-}egrep\ 
X  echo not found: DISABLE UNSUBSCRIBE REQUESTS
X  should_have=${should_have:-}egrep\ 
Xelse
X  echo found
Xfi
Xecho $n "tail: $c"
Xif [ "`which tail | grep '^/'`" = "" ]; then
X  missing=${missing:-}tail\ 
X  echo not found: not too important
X  should_have=${should_have:-}tail\ 
Xelse
X  echo found
Xfi
Xecho $n "uptime: $c"
Xif [ "`which uptime | grep '^/'`" = "" ]; then
X  missing=${missing:-}uptime\ 
X  echo not found: load restrictions may not be posed
X  should_have=${should_have:-}uptime\ 
Xelse
X  echo found
Xfi
Xecho $n "ps: $c"
Xif [ "`which ps | grep '^/'`" = "" ]; then
X  missing=${missing:-}ps\ 
X  echo not found: load restrictions may not be posed
X  should_have=${should_have:-}ps\ 
Xelse
X  echo $n "found: $c"
X  ps -ef > /dev/null 2>&1
X  if [ $? -eq 0 ]; then
X    echo System V version\; use the \'option sysv_ps\' in the config file
X  else
X    echo BSD version\; use the \'option bsd_ps\' in the config file
X  fi
Xfi
Xecho $n "chmod: $c"
Xif [ "`which chmod | grep '^/'`" = "" ]; then
X  missing=${missing:-}chmod\ 
X  echo not found: load restrictions may not be posed
X  should_have=${should_have:-}chmod\ 
Xelse
X  echo found
Xfi
Xecho $n "sort: $c"
Xif [ "`which sort | grep '^/'`" = "" ]; then
X  missing=${missing:-}sort\ 
X  echo not found: statistics will not be sorted and .subscribers files will not
Xbe sorted by domain
X  should_have=${should_have:-}sort\ 
Xelse
X  echo found
Xfi
Xecho $n "uniq: $c"
Xif [ "`which uniq | grep '^/'`" = "" ]; then
X  missing=${missing:-}uniq\
X  echo not found: output of 'statistics -all' will be bogus
X  should_have=${should_have:-}uniq\ 
Xelse
X  echo found
Xfi
Xif [ $missing ]; then
X  echo Required UNIX utilities missing: $missing
Xfi
Xif [ $workaround ]; then
X  echo Missing UNIX utilities for which a workaround exists: $workaround
Xfi
Xif [ $should_have ]; then
X  echo Missing UNIX utilities you should have: $should_have
Xfi
Xif [ $have_to_have ]; then
X  echo Missing UNIX utilities you have to have: $have_to_have
Xfi
Xecho
X
Xecho --- Special compilation options:
Xif [ -f /usr/include/sys/select.h ]; then
X  echo '*** Compile with -DHAVE_SELECT_H'
Xelse
X  file=`(cd /usr/include;\
X	 find . -name select.h -print 2>/dev/null)` #find -follow not supported on all platforms
X  if [ "$file" = "" ]; then
X    echo '*** Do NOT compile with the -DHAVE_SELECT_H flag'
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo '*** Compile with -DHAVE_SELECT_H'
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g' |\
Xsed 's/\/sys//g'`
X    echo $file | grep 'sys/select.h' > /dev/null 2>&1
X    if [ $? -eq 1 ]; then
X      echo '*** Change #include <sys/select.h> to #include <select.h> in the source files'
X    fi
X  fi
Xfi
Xif [ -f /usr/include/sys/ulimit.h -o -f /usr/include/ulimit.h ]; then
X  echo '*** Compile with -DHAVE_ULIMIT_H'
Xelse
X  file=`(cd /usr/include;\
X	 find . -name ulimit.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    echo '*** Do NOT compile with the -DHAVE_ULIMIT_H flag'
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo '*** Compile with -DHAVE_ULIMIT_H'
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g'`
X  fi
Xfi
Xif [ -f /usr/include/setjmp.h ]; then
X  echo '*** Compile with -DHAVE_SETJMP_H'
Xelse
X  file=`(cd /usr/include;\
X	 find . -name setjmp.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    echo '*** Do NOT compile with the -DHAVE_SETJMP_H flag'
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo '*** Compile with -DHAVE_SETJMP_H'
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g'`
X  fi
Xfi
Xif [ -f /usr/include/tzfile.h ]; then
X  echo '*** Compile with -DHAVE_TZFILE_H'
Xelse
X  file=`(cd /usr/include;\
X	 find . -name tzfile.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    echo '*** Do NOT compile with the -DHAVE_TZFILE_H flag'
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo '*** Compile with -DHAVE_TZFILE_H'
X    echo \*\*\* Compile with -I/usr/server`dirnam $file | sed 's/\.//g'`
X  fi
Xfi
Xif [ -f /usr/include/syslog.h ]; then
X  echo '*** You may wish to compile with -DSYSLOG=facility to use syslog(3) for reports'
Xelse
X  file=`(cd /usr/include;\
X	 find . -name syslog.h -print 2>/dev/null)`
X  if [ "$file" != "" ]; then
X    file=`echo $file | awk '{ print $1 }'`
X    echo '*** You may wish to compile with -DSYSLOG=facility to use syslog(3) for reports'
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g'`
X  fi
Xfi
Xcat > c.c << EOF
X#include <stdio.h>
X#ifdef __STDC__
X#include <stdarg.h>
Xint f (char *, ...);
X#else
X#include <varargs.h>
Xint f ();
X#endif
X
Xmain ()
X{
X  f ("%s", "test");
X}
X
X#ifdef __STDC__
Xf (char *s, ...)
X#else
Xf (s, va_alist)
Xchar *s;
Xva_dcl
X#endif
X{
X  char buf[80];
X  va_list ap;
X
X#ifdef __STDC__
X  va_start (ap, s);
X#else
X  va_start (ap);
X#endif
X  vsprintf (buf, s, ap);
X  va_end (ap);
X}
XEOF
X$CC c.c > /dev/null 2>&1
Xif [ $? -ne 0 ]; then
X  echo '*** Compile with -DNEED_VSPRINTF'
Xfi
Xrm -f a.out c.c c.o > /dev/null 2>&1
X
Xecho
X
Xecho --- Looking for specific header files:
X
Xgrep SIGCLD /usr/include/sys/signal.h > /dev/null 2>&1
Xsigcld1=$?
Xgrep SIGCHLD /usr/include/sys/signal.h > /dev/null 2>&1
Xsigcld2=$?
Xgrep SIGUSR1 /usr/include/sys/signal.h > /dev/null 2>&1
Xsigusr1=$?
Xgrep SIGUSR2 /usr/include/sys/signal.h > /dev/null 2>&1
Xsigusr2=$?
Xif [ $sigcld1 -ne 0 -o $sigcld2 -ne 0 -o $sigusr1 -ne 0 -o $sigusr2 -ne 0 ]; then
X    go_interactive=no${known_port:-'; signals missing'}
Xelse
X  if [ -f /usr/include/sys/wait.h ]; then
X    grep waitpid /usr/include/sys/wait.h > /dev/null 2>&1
X    if [ $? -gt 0 ]; then
X      echo WARNING: Could not find waitpid\(\) in /usr/include/sys/wait.h
X      echo If you do not have this function nor wait3\(\), undefine the symbol
X      echo GO_INTERACTIVE in src/defs.h\; if you have wait3\(\), see serverd.c to
X      echo see how it is used on systems that have wait3\(\) only.
X      go_interactive=maybe
X    fi
X  else
X    file=`(cd /usr/include;\
X	   find . -name wait.h -print 2>/dev/null)`
X    if [ "$file" = "" ]; then
X      echo '*** Remove #include <sys/wait.h> from all source files'
X    else
X      file=`echo $file | awk '{ print $1 }'`
X      echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g' |\
Xsed 's/\/sys//g'`
X      echo $file | grep 'sys/wait.h' > /dev/null 2>&1
X      if [ $? -eq 1 ]; then
X        echo '*** Change #include <sys/wait.h> to #include <wait.h> in the source files'
X      fi
X      grep waitpid $file > /dev/null 2>&1
X      if [ $? -gt 0 ]; then
X        echo WARNING: Could not find waitpid\(\) in $file
X        echo If you do not have this function nor wait3\(\), undefine the symbol
X        echo GO_INTERACTIVE in src/defs.h\; if you have wait3\(\), see serverd.c to
X        echo see how it is used on systems that have wait3\(\) only.
X        go_interactive=maybe
X      fi
X    fi
X  fi
Xfi
Xif [ ! -f /usr/include/sys/sem.h ]; then
X  file=`(cd /usr/include;\
X	 find . -name sem.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    go_interactive=no${known_port:-'; no semaphore support; undefine the symbol GO_INTERACTIVE in src/Makefile'}
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g' |\
Xsed 's/\/sys//g'`
X    echo $file | grep 'sys/sem.h' > /dev/null 2>&1
X    if [ $? -eq 1 ]; then
X      echo '*** Change #include <sys/sem.h> to #include <sem.h> in the source files'
X    fi
X  fi
Xfi
Xif [ ! -f /usr/include/netdb.h ]; then
X  file=`(cd /usr/include;\
X	 find . -name netdb.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    echo '*** Remove #include <netdb.h> from all source files'
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo \*\*\* Compile with -I/usr/include`dirnam $file | sed 's/\.//g'`
X  fi
Xfi
Xif [ ! -f /usr/include/sys/socket.h ]; then
X  file=`(cd /usr/include;\
X	 find . -name socket.h -print 2>/dev/null)`
X  if [ "$file" = "" ]; then
X    tcp_ip=no${known_port:-'; undefine the symbol TCP_IP in defs.h; the system mailmethod may not be used'}
X  else
X    file=`echo $file | awk '{ print $1 }'`
X    echo \*\*\* Compile with -I/usr/server`dirnam $file | sed 's/\.//g' |\
Xsed 's/\/sys//g'`
X    echo $file | grep 'sys/socket.h' > /dev/null 2>&1
X    if [ $? -eq 1 ]; then
X      echo '*** Change #include <sys/socket.h> to #include <socket.h> in the source files'
X    fi
X  fi
Xfi
X
Xecho The system can go interactive: ${go_interactive:-"yes${known_port:- compile with -DGO_INTERACTIVE}"}
Xecho TCP/IP present: ${tcp_ip:-"yes (system mailmethod should be used if using SMTP)"}
Xecho
X
Xecho $n "--- Looking for inews: $c"
Xif [ ! -r /usr/lib/news/inews ]; then
X  echo /usr/lib/news/inews not found: see src/Makefile
Xelse
X  echo found
Xfi
Xecho $n "--- Looking for UCB mail: $c"
Xif [ ! -r /usr/ucb/mail ]; then
X  ucbmail1=`which mailx`
X  ucbmail2=`which Mail`
X  if [ "`echo $ucbmail1 | grep '^/'`" != "" ]; then 
X    echo compile with -DUCB_MAIL=\\\"$ucbmail1\\\"\; use the \'option bsd_mail\' in the config file
X  else
X    if [ "`echo $ucbmail2 | grep '^/'`" != "" ]; then
X      echo compile with -DUCB_MAIL=$ucbmail2\; use the \'option bsd_mail\' in the config file
X    else
X      echo not found\; the manager will not be notified of error conditions
X    fi
X  fi
Xelse
X  echo found\; nothing you have to do
Xfi
Xecho 
X
Xecho $n "--- Testing the compiler: $c"
Xcat > c.c << EOF
X#include <stdio.h>
X#define PATH "/tmp" "/tmp"
X
Xmain (int argc, char **argv) {
X#ifndef __STDC__
X  printf ("nonstdc\n");
X#endif
X}
XEOF
X
X$CC c.c > /dev/null 2>&1
Xif [ $? -ne 0 ]; then
X  echo You seem to have a non-ANSI compiler. Check the man
X  echo page for cc and src/Makefile for possibilities/suggestions for flags that will
X  echo turn it into an ANSI-C compiler. Otherwise you have to use unproto\; see
X  echo src/Makefile
X  echo If you have an ANSI-C compiler change the symbol CC in this script and run again 
X  ansi_compiler=non-ANSI
Xelif [ "`./a.out`" = "nonstdc" ]; then
X  echo You seem to have a hybrid compiler which understands 
X  echo ANSI C but does not define the __STDC__ macro\; see src/Makefile
X  ansi_compiler=hybrid
Xelse
X  echo It\'s an ANSI-C compiler\; good
X  ansi_compiler=ANSI-C
Xfi
Xif [ "`which gcc | grep '^/'`" != "" ]; then
X  echo If you decide to use gcc, use the -fwritable-strings flag
Xfi
Xif [ "$host" != "" ]; then
X  echo See src/Makefile for compilation options
Xfi
Xif [ "$ansi_compiler" = "non-ANSI" -o \
X     "$ansi_compiler" = "hybrid" ]; then
X  echo $n "--- Testing the preprocessor: $c"
X  cat > c.c << EOF
X#include <stdio.h>
X#define PATH "/tmp
X#define FILE PATH/file"
X
Xmain ()
X{
X  printf ("%s", PATH");
X}
XEOF
X
X  $CC -E c.c > /dev/null 2>&1
X  if [ $? -ne 0 ]; then
X    if [ "$ansi_compiler" = "non-ANSI" ]; then
X      echo You need to use stds\; get it from cs.bu.edu in pub/listserv/utils,
X      echo or use the cpp that comes with the unproto package
X    else
X      echo '*** Compile with -D__STDC__'
X    fi
X  else
X    echo It does string concatenations\; good
X  fi
X
X  cat > c.c << EOF
X#include <stdio.h>
Xmain ()
X{
X#ifdef (very)
X  (void) 0;
X#elif defined (old) || defined (preproc)
X  (void) 0;
X#endif
X}
XEOF
X  $CC -E c.c > /dev/null 2>&1
X  if [ $? -ne 0 ]; then
X    if [ "$ansi_compiler" = "non-ANSI" ]; then
X      echo '### FATAL: your preprocessor does not understand the #elif directive'
X      echo You should use the one that comes with the unproto package
X    fi
X  fi
Xelse
X  cat > c.c << EOF
X#include <stdio.h>
X 
Xmain ()
X{
X  int f();
X  f ("test");
X  exit (0);
X}
X 
Xf (char *s)
X{
X  *s='a';
X}
XEOF
X  $CC c.c > /dev/null 2>&1
X  ./a.out > /dev/null 2>&1
X  if [ $? -ne 0 ]; then
X    rm -f core
X    echo WARNING: you need to compile with a flag that will force the compiler to
X    echo place constant strings into a writable area.
X  fi
Xfi
Xecho
X
Xecho $n "--- Testing the system: $c"
Xcat > c.c << EOF
X#include <stdio.h>
X
Xmain ()
X{
X  printf ("%d\n", sizeof (int));
X}
XEOF
X
X$CC c.c > /dev/null 2>&1
Xif [ `./a.out` -lt 4 ]; then
X  echo '### FATAL: integers on this machine are less than 32 bits long'
Xfi
X
Xcat > c.c << EOF
X#include <stdio.h>
X#include <signal.h>
X
Xmain ()
X{
X  sighold (SIGHUP);
X  exit (0);
X}
XEOF
X
X$CC c.c > /dev/null 2>&1
X
Xif [ ! -f a.out ]; then
X  echo "It's got BSD flavor; *** Compile with -Dbsd"
X  sys_type=bsd
Xelse
X  ./a.out > /dev/null 2>&1
X  if [ $? -ne 0 ]; then
X    echo It\'s got BSD flavor${known_port:-'; compile with -Dbsd'}
X    sys_type=bsd
X  else
X    echo It\'s got System V flavor${known_port:-'; compile with -Dsvr3 or -Dsvr4'}
X    sys_type=sysv
X  fi
Xfi
X
Xcat > c.c << EOF
X#include <stdio.h>
X#include <signal.h>
X
Xint child_exited = 0, pid;
X
Xmain ()
X{
X  int sighandle (), wakeup (), mask;
X#ifdef SIGCLD
X  signal (SIGCLD, (void (*)()) sighandle);
X#else
X  signal (SIGCHLD, (void (*)()) sighandle);
X#endif
X  signal (SIGALRM, (void (*)()) wakeup);
X  alarm (3);
X  if ((pid = fork()) < 0)
X    exit (2);
X  else if (pid == 0) {
X#ifdef bsd
X# ifdef SIGCLD
X    mask = sigblock (sigmask (SIGCLD));
X# else
X    mask = sigblock (sigmask (SIGCHLD));
X# endif
X#else
X# ifdef SIGCLD
X    sighold (SIGCLD);
X# else
X    sighold (SIGCHLD);
X# endif
X#endif
X    system ("cat < /dev/null");
X    _exit (0);
X  }
X  else
X    while (1);
X}
X
Xint wakeup (sig)
Xint sig;
X{
X  signal (sig, SIG_IGN);
X  if (child_exited)
X    exit (0);
X  kill (pid, SIGKILL);
X  exit (1);
X}
X
Xint sighandle (sig)
Xint sig;
X{
X  child_exited = 1;
X  wakeup (SIGALRM);
X}
XEOF
X
X$CC -D$sys_type c.c > /dev/null 2>&1
X
Xif [ -f a.out ]; then
X  ./a.out
X  ex=$?
X  if [ $ex -eq 1 ]; then
X    if [ "${go_interactive:-yes}" != "yes" ]; then
X      echo 'WARNING: Your system() is buggy; cannot block SIGCHLD before using system()'
X      echo It is recommended that the system does not go interactive
X    fi
X  elif [ $ex -eq 2 ]; then
X    echo 'WARNING: cannot fork(); I suspect error in the compiler flags'
X  else
X    echo 'system() works OK'
X  fi
Xelse
X  echo WARNING: severely limited system\; basic system calls missing
X  echo "*** DO NOT USE THE -D$sys_type flag ***"
Xfi
X
Xif [ "$host" != "" ]; then
X  echo "*** Consult doc/server.nr for more information on your $host system"
Xfi
X
Xrm -f c.c c.o a.out > /dev/null 2>&1
X
Xecho $n "Non-blocking I/O support: $c"
Xex1=1
Xfor i in /usr/include/*.h; do
X  if [ -r $i -a $ex1 -ne 0 ]; then
X    grep O_NDELAY $i > /dev/null 2>&1
X    ex1=$?
X  fi
Xdone
Xif [ $ex1 -eq 1 ]; then
X  ex2=1
X  for i in /usr/include/sys/*.h; do
X    if [ -r $i -a $ex2 -ne 0 ]; then
X      grep O_NDELAY $i > /dev/null 2>&1
X      ex2=$?
X    fi
X  done
Xfi
Xif [ $ex1 -eq 0 -o ${ex2:-1} -eq 0 ]; then
X  echo yes
Xelse
X  for i in `(cd /usr/include; find . -print 2>/dev/null)`; do
X    grep O_NDELAY /usr/include/$i > /dev/null 2>&1
X    if [ $? -eq 0 ]; then
X      echo yes
X      echo \*\*\* Compile with -I/usr/include`dirnam $i | sed 's/\.//g'`
X      found=1
X    fi
X  done
X  if [ ${found:-0} -eq 0 ]; then
X    echo no${known_port:-'; undefine NONBLOCKING_IO in src/defs.h'}
X  fi
Xfi
X
Xecho $n "File locking support: $c"
Xex1=1
Xfor i in /usr/include/*.h; do
X  if [ -r $i -a $ex1 -ne 0 ]; then
X    grep F_TLOCK $i > /dev/null 2>&1
X    ex1=$?
X  fi
Xdone
Xif [ $ex1 -eq 1 ]; then
X  ex2=1
X  for i in /usr/include/sys/*.h; do
X    if [ -r $i -a $ex2 -ne 0 ]; then
X      grep F_TLOCK $i > /dev/null 2>&1
X      ex2=$?
X    fi
X  done
Xfi
Xif [ $ex1 -eq 0 -o ${ex2:-1} -eq 0 ]; then
X  echo yes${known_port:-'; make sure NO_LOCKS is NOT defined in src/defs.h'}
Xelse
X  for i in `(cd /usr/include; find . -print 2>/dev/null)`; do
X    grep F_TLOCK /usr/include/$i > /dev/null 2>&1
X    if [ $? -eq 0 ]; then
X      echo yes
X      echo \*\*\* Compile with -I/usr/include`dirnam $i | sed 's/\.//g'`
X      found=1
X    fi
X  done
X  if [ ${found:-0} -eq 0 ]; then
X    echo no${known_port:-'; *** Compile with -DNO_LOCKS'}
X  fi
Xfi
Xexit 0
*-*-END-of-systest-*-*
exit
